roar
Loading...
Searching...
No Matches
server.hpp
Go to the documentation of this file.
1#pragma once
2
6#include <roar/error.hpp>
8#include <roar/request.hpp>
14
15#include <boost/describe/modifiers.hpp>
16#include <boost/asio/ssl/context.hpp>
17#include <boost/describe/members.hpp>
18#include <boost/leaf.hpp>
19#include <boost/mp11/algorithm.hpp>
20#include <boost/beast/http/verb.hpp>
21
22#include <memory>
23#include <string>
24#include <tuple>
25#include <unordered_map>
26#include <functional>
27#include <iterator>
28#include <variant>
29
30namespace Roar
31{
32 class RequestListener;
33 class Server
34 {
35 public:
37 {
39 boost::asio::any_io_executor executor;
40
42 std::optional<std::variant<SslServerContext, boost::asio::ssl::context>> sslContext;
43
45 std::function<void(Error&&)> onError = [](auto&&) {};
46
48 std::function<void(boost::system::error_code)> onAcceptAbort = [](auto) {};
49
52 std::unique_ptr<StandardResponseProvider> standardResponseProvider =
53 std::make_unique<StandardTextResponseProvider>();
54 };
55
61 Server(ConstructionArguments constructionArgs);
62
63 ~Server();
64 Server(Server const&) = delete;
66 Server& operator=(Server const&) = delete;
68
75 boost::leaf::result<void> start(unsigned short port = 0, std::string const& host = "::");
76
82 boost::leaf::result<void> start(boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> const& bindEndpoint);
83
90
94 void stop();
95
101 template <typename RequestListenerT, typename... ConstructionArgsT>
102 std::shared_ptr<RequestListenerT> installRequestListener(ConstructionArgsT&&... args)
103 {
104 auto listener = std::make_shared<RequestListenerT>(std::forward<ConstructionArgsT>(args)...);
105 using routes = boost::describe::
106 describe_members<RequestListenerT, boost::describe::mod_any_access | boost::describe::mod_static>;
107 std::unordered_multimap<boost::beast::http::verb, ProtoRoute> extractedRoutes;
108 boost::mp11::mp_for_each<routes>([&extractedRoutes, &listener, this]<typename T>(T route) {
109 this->addRoute(listener, extractedRoutes, *route.pointer);
110 });
111 addRequestListenerToRouter(std::move(extractedRoutes));
112 return listener;
113 }
114
121 bool isSecure() const;
122
128 boost::asio::any_io_executor getExecutor() const;
129
130 private:
131 template <typename RequestListenerT>
133 std::shared_ptr<RequestListenerT> listener,
134 std::unordered_multimap<boost::beast::http::verb, ProtoRoute>& extractedRoutes,
135 RouteInfo<RequestListenerT> const& info)
136 {
137 ProtoRoute protoRoute{};
138 switch (info.pathType)
139 {
141 {
142 protoRoute.path = std::string{info.path};
143 protoRoute.matches = [p = info.path](std::string const& path, std::vector<std::string>&) {
144 return path == p;
145 };
146 break;
147 }
149 {
150 protoRoute.path = std::regex{info.path};
151 protoRoute.matches =
152 [p = info.path](std::string const& path, std::vector<std::string>& regexMatches) {
153 std::smatch smatch;
154 auto result = std::regex_match(path, smatch, std::regex{p});
155 regexMatches.reserve(smatch.size());
156 for (auto submatch = std::next(std::begin(smatch)), end = std::end(smatch); submatch < end;
157 ++submatch)
158 regexMatches.push_back(submatch->str());
159 return result;
160 };
161 break;
162 }
163 default:
164 throw std::runtime_error("Invalid path type for route.");
165 }
166 protoRoute.routeOptions = info.routeOptions;
167 protoRoute.callRoute = [serverIsSecure = isSecure(),
168 listener,
169 handler = info.handler,
170 allowUnsecure = protoRoute.routeOptions.allowUnsecure](
172 if (serverIsSecure && !session.isSecure() && !allowUnsecure)
173 return session.sendStrictTransportSecurityResponse();
174 std::invoke(handler, *listener, session, std::move(req));
175 };
176 extractedRoutes.emplace(*info.verb, protoRoute);
177
178 if (protoRoute.routeOptions.cors && protoRoute.routeOptions.cors->generatePreflightOptionsRoute)
179 {
180 auto preflightRoute = protoRoute;
181 protoRoute.callRoute = [serverIsSecure = isSecure(),
182 allowUnsecure = protoRoute.routeOptions.allowUnsecure](
184 if (serverIsSecure && !session.isSecure() && !allowUnsecure)
185 return session.sendStrictTransportSecurityResponse();
186 session.send<boost::beast::http::empty_body>(session.prepareResponse(req))->commit();
187 };
188 extractedRoutes.emplace(boost::beast::http::verb::options, preflightRoute);
189 }
190 }
191
192 template <typename RequestListenerT>
194 std::shared_ptr<RequestListenerT> listener,
195 std::unordered_multimap<boost::beast::http::verb, ProtoRoute>& extractedRoutes,
196 ServeInfo<RequestListenerT> const& info)
197 {
198 namespace http = boost::beast::http;
199
200 ProtoRoute protoRoute{};
201 protoRoute.path = Detail::ServedPath{std::string{info.path}};
202 protoRoute.routeOptions = info.routeOptions;
203 protoRoute.callRoute = [dserver = Detail::DirectoryServer<RequestListenerT>{{
205 .serveInfo_ = info,
206 .listener_ = listener,
207 }}](Session& session, Request<http::empty_body> const& req) {
208 auto dserverCpy = dserver;
209 dserverCpy(session, req);
210 };
211 protoRoute.matches = [p = info.path](std::string const& path, std::vector<std::string>&) {
212 return path.starts_with(p);
213 };
214 // Add the all regardeless of allowed or not, to give a proper response.
215 extractedRoutes.emplace(http::verb::options, protoRoute);
216 extractedRoutes.emplace(http::verb::head, protoRoute);
217 extractedRoutes.emplace(http::verb::get, protoRoute);
218 extractedRoutes.emplace(http::verb::put, protoRoute);
219 extractedRoutes.emplace(http::verb::delete_, protoRoute);
220 }
221
222 void addRequestListenerToRouter(std::unordered_multimap<boost::beast::http::verb, ProtoRoute>&& routes);
223
224 private:
225 struct Implementation;
226 std::shared_ptr<Implementation> impl_;
227 };
228}
static constexpr int port
Definition main.cpp:16
Definition request_listener.hpp:8
Internal helper class to serve directories.
Definition directory_server.hpp:63
This class extends the boost::beast::http::request<BodyT> with additional convenience.
Definition request.hpp:52
Definition server.hpp:34
Server & operator=(Server &&)
boost::leaf::result< void > start(boost::asio::ip::basic_endpoint< boost::asio::ip::tcp > const &bindEndpoint)
Starts the server given the already resolved bind endpoint.
Server(Server const &)=delete
void addRoute(std::shared_ptr< RequestListenerT > listener, std::unordered_multimap< boost::beast::http::verb, ProtoRoute > &extractedRoutes, RouteInfo< RequestListenerT > const &info)
Definition server.hpp:132
boost::asio::any_io_executor getExecutor() const
Get the Executor object.
Definition server.cpp:105
std::shared_ptr< RequestListenerT > installRequestListener(ConstructionArgsT &&... args)
Attach a request listener to this server to receive requests.
Definition server.hpp:102
boost::leaf::result< void > start(unsigned short port=0, std::string const &host="::")
Bind and listen for network interface and port.
Definition server.cpp:110
boost::asio::ip::basic_endpoint< boost::asio::ip::tcp > const & getLocalEndpoint() const
Get the local endpoint that this server bound to.
Definition server.cpp:116
void stop()
Stop and shutdown the server.
Definition server.cpp:148
Server(Server &&)
~Server()
Definition server.cpp:100
std::shared_ptr< Implementation > impl_
Definition server.hpp:226
void addRoute(std::shared_ptr< RequestListenerT > listener, std::unordered_multimap< boost::beast::http::verb, ProtoRoute > &extractedRoutes, ServeInfo< RequestListenerT > const &info)
Definition server.hpp:193
Server & operator=(Server const &)=delete
void addRequestListenerToRouter(std::unordered_multimap< boost::beast::http::verb, ProtoRoute > &&routes)
Definition server.cpp:154
bool isSecure() const
Returns whether this server has a certificate and key and is therefore a HTTPS server.
Definition server.cpp:159
Definition session.hpp:41
Definition forward.hpp:18
Definition authorization.hpp:10
@ RegularString
Take precedence over any other path.
@ Regex
Take precedence over serve paths.
bool serverIsSecure_
Definition directory_server.hpp:51
Definition proto_route.hpp:32
Holds errors that are produced asynchronously anywhere.
Definition error.hpp:20
A proper Route is built up from this object. This object is usually created internally.
Definition proto_route.hpp:45
std::variant< std::string, std::regex, Detail::ServedPath > path
Definition proto_route.hpp:46
This class is what is used in ROAR_GET, ROAR_PUT, ... requests All options you can set for routes beh...
Definition request_listener.hpp:120
HandlerType< RequestListenerT > handler
Set automatically by the macro.
Definition request_listener.hpp:130
RoutePathType pathType
Is the path a string or a regex, ...?
Definition request_listener.hpp:127
std::optional< boost::beast::http::verb > verb
What verb for this route?
Definition request_listener.hpp:124
Definition request_listener.hpp:135
Definition server.hpp:37
std::optional< std::variant< SslServerContext, boost::asio::ssl::context > > sslContext
Supply for SSL support.
Definition server.hpp:42
std::unique_ptr< StandardResponseProvider > standardResponseProvider
Definition server.hpp:52
boost::asio::any_io_executor executor
Required io executor for boost::asio.
Definition server.hpp:39
std::function< void(boost::system::error_code)> onAcceptAbort
Called when the server stops accepting connections for error reasons.
Definition server.hpp:48
std::function< void(Error &&)> onError
Called when an error occurs in an asynchronous routine.
Definition server.hpp:45