magpie
Loading...
Searching...
No Matches
Router.hpp
1#pragma once
2
3#include "magpie/AppDecl.hpp"
4#include "magpie/data/CommonData.hpp"
5#include "magpie/dsa/RadixTree.hpp"
6#include "magpie/middlewares/MiddlewareProcessor.hpp"
7#include "magpie/routing/BaseRouter.hpp"
8#include "magpie/routing/Compile.hpp"
9#include "magpie/routing/BaseRoute.hpp"
10#include "magpie/routing/Route.hpp"
11#include "magpie/transfer/Request.hpp"
12#include "magpie/transfer/Response.hpp"
13#include "magpie/transfer/StatusCode.hpp"
14
15#include <memory>
16#include <string_view>
17#include <variant>
18
19namespace magpie::routing {
20
21template <data::IsCommonData ContextType>
22class Router : public BaseRouter {
23private:
25public:
26 template <FixedString path>
27 BaseRoute<ContextType>* registerRoute(
28 const RouteCallback<path, ContextType>& callback,
29 Method::HttpMethod method
30 ) {
31 std::shared_ptr<BaseRoute<ContextType>> route = std::make_shared<Route<path, ContextType>>(
32 callback
33 );
34 auto splitPath = pathToComponents(path.c_str());
35
36 this->routes.pushRoute(route, method, splitPath);
37 return route.get();
38 }
39
40 constexpr void normalisePath(
41 const std::string_view& path,
42 std::string_view& normPath,
43 std::string_view& rawGetArgs
44 ) const {
45 if (auto pos = path.find_first_of('?'); pos != std::string::npos) {
46 rawGetArgs = path.substr(pos);
47 normPath = path.substr(0, pos);
48 } else {
49 normPath = path;
50 }
51 }
52
53 constexpr std::vector<std::string_view> pathToComponents(const std::string_view& path) const {
54 size_t next;
55 size_t start = 0;
56 std::vector<std::string_view> out {
57 "/"
58 };
59 while ((next = path.find('/', start + 1)) != std::string::npos) {
60 if (next - start == 1) { // `/segment//whatever`
61 start = next;
62 } else if (next != start) { // /[test/] (selection in brackets)
63 out.push_back(path.substr(start + 1, (next - start - 1)));
64 out.push_back("/");
65 start = next;
66 }
67 }
68
69 // No trailing slash, so pick up whatever's left
70 if (start < path.size() - 1) {
71 out.push_back(path.substr(start + 1));
72 }
73
74 return out;
75 }
76
77 void invokeRoute(
78 const std::string& path,
80 Request& req,
81 Response& res
82 ) const override {
83 // Compact
84
85 if (path.size() == 0 || path[0] != '/') {
86 [[unlikely]]
87 throw std::runtime_error("Invalid route supplied");
88 }
89
90 std::string_view normPath;
91 std::string_view rawGetArgs;
92 normalisePath(path, normPath, rawGetArgs);
93 auto segments = this->pathToComponents(normPath);
94
95 // TODO: handle query params
96 auto callback = this->routes.getRoute(segments, req.method);
97
98 std::visit([&res, &req, ctx, &segments](auto& it) {
99 using T = std::decay_t<decltype(it)>;
100 auto* contextApp = static_cast<ContextApp<ContextType>*>(ctx->app);
101 auto* castCtx = static_cast<ContextType*>(ctx);
102 if constexpr (std::is_same_v<dsa::FindError, T>) {
103 contextApp->notFoundErrorHandler->onRouteNotFound(castCtx, req, res, it);
104 } else {
105 contextApp->errorHandler->tryCall(
106 castCtx, req, res, [&]() {
107 auto* route = it.get();
109 route,
110 std::vector<Middlewares<ContextType>*> {
111 contextApp->getMiddlewaresAsPtr(),
112 route->getMiddlewaresAsPtr(),
113 },
114 segments,
115 castCtx,
116 req,
117 res
118 ).invokeRoute();
119
120 }
121 );
122 }
123 }, callback);
124 }
125};
126
127}
Definition AppDecl.hpp:37
Definition MiddlewareProcessor.hpp:10
Definition RadixTree.hpp:116
std::variant< FindError, Value > getRoute(const std::vector< std::string_view > &route, Method::HttpMethod method) const
Definition RadixTree.hpp:131
Definition BaseRoute.hpp:16
Definition BaseRouter.hpp:9
Definition Router.hpp:22
Definition Middleware.hpp:145
Definition Request.hpp:11
Method::HttpMethod method
Definition Request.hpp:35
Definition Response.hpp:13
Definition CommonData.hpp:8