1 | // Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com> |
2 | // Copyright (C) 2019 The Qt Company Ltd. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
4 | |
5 | #ifndef QHTTPSERVER_H |
6 | #define QHTTPSERVER_H |
7 | |
8 | #include <QtHttpServer/qabstracthttpserver.h> |
9 | #include <QtHttpServer/qhttpserverrouter.h> |
10 | #include <QtHttpServer/qhttpserverrouterrule.h> |
11 | #include <QtHttpServer/qhttpserverresponse.h> |
12 | #include <QtHttpServer/qhttpserverrouterviewtraits.h> |
13 | #include <QtHttpServer/qhttpserverviewtraits.h> |
14 | |
15 | #if QT_CONFIG(future) |
16 | # include <QtCore/qfuture.h> |
17 | #endif |
18 | |
19 | #include <functional> |
20 | #include <tuple> |
21 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | class QHttpServerRequest; |
25 | |
26 | class QHttpServerPrivate; |
27 | class Q_HTTPSERVER_EXPORT QHttpServer final : public QAbstractHttpServer |
28 | { |
29 | Q_OBJECT |
30 | Q_DECLARE_PRIVATE(QHttpServer) |
31 | |
32 | template<int I, typename ... Ts> |
33 | struct VariadicTypeAt { using Type = typename std::tuple_element<I, std::tuple<Ts...>>::type; }; |
34 | |
35 | template<typename ... Ts> |
36 | struct VariadicTypeLast { |
37 | using Type = typename VariadicTypeAt<sizeof ... (Ts) - 1, Ts...>::Type; |
38 | }; |
39 | |
40 | template <typename...> |
41 | static constexpr bool dependent_false_v = false; |
42 | |
43 | template<typename T> |
44 | using ResponseType = |
45 | #if QT_CONFIG(future) |
46 | std::conditional_t< |
47 | std::is_same_v<QFuture<QHttpServerResponse>, T>, |
48 | QFuture<QHttpServerResponse>, |
49 | QHttpServerResponse |
50 | >; |
51 | #else |
52 | QHttpServerResponse; |
53 | #endif |
54 | |
55 | public: |
56 | explicit QHttpServer(QObject *parent = nullptr); |
57 | ~QHttpServer(); |
58 | |
59 | QHttpServerRouter *router(); |
60 | |
61 | template<typename Rule = QHttpServerRouterRule, typename ... Args> |
62 | bool route(Args && ... args) |
63 | { |
64 | using ViewHandler = typename VariadicTypeLast<Args...>::Type; |
65 | using ViewTraits = QHttpServerRouterViewTraits<ViewHandler>; |
66 | static_assert(ViewTraits::Arguments::StaticAssert, |
67 | "ViewHandler arguments are in the wrong order or not supported" ); |
68 | return routeHelper<Rule, ViewHandler, ViewTraits>( |
69 | QtPrivate::makeIndexSequence<sizeof ... (Args) - 1>{}, |
70 | std::forward<Args>(args)...); |
71 | } |
72 | |
73 | template<typename ViewHandler> |
74 | void afterRequest(ViewHandler &&viewHandler) |
75 | { |
76 | using ViewTraits = QHttpServerAfterRequestViewTraits<ViewHandler>; |
77 | static_assert(ViewTraits::Arguments::StaticAssert, |
78 | "ViewHandler arguments are in the wrong order or not supported" ); |
79 | afterRequestHelper<ViewTraits, ViewHandler>(std::move(viewHandler)); |
80 | } |
81 | |
82 | using MissingHandler = std::function<void(const QHttpServerRequest &request, |
83 | QHttpServerResponder &&responder)>; |
84 | |
85 | void setMissingHandler(MissingHandler handler); |
86 | |
87 | private: |
88 | using AfterRequestHandler = |
89 | std::function<QHttpServerResponse(QHttpServerResponse &&response, |
90 | const QHttpServerRequest &request)>; |
91 | |
92 | template<typename ViewTraits, typename ViewHandler> |
93 | void afterRequestHelper(ViewHandler &&viewHandler) { |
94 | auto handler = [viewHandler](QHttpServerResponse &&resp, |
95 | const QHttpServerRequest &request) { |
96 | if constexpr (ViewTraits::Arguments::Last::IsRequest::Value) { |
97 | if constexpr (ViewTraits::Arguments::Count == 2) |
98 | return std::move(viewHandler(std::move(resp), request)); |
99 | else |
100 | static_assert(dependent_false_v<ViewTraits>); |
101 | } else if constexpr (ViewTraits::Arguments::Last::IsResponse::Value) { |
102 | if constexpr (ViewTraits::Arguments::Count == 1) |
103 | return std::move(viewHandler(std::move(resp))); |
104 | else if constexpr (ViewTraits::Arguments::Count == 2) |
105 | return std::move(viewHandler(request, std::move(resp))); |
106 | else |
107 | static_assert(dependent_false_v<ViewTraits>); |
108 | } else { |
109 | static_assert(dependent_false_v<ViewTraits>); |
110 | } |
111 | }; |
112 | |
113 | afterRequestImpl(afterRequestHandler: std::move(handler)); |
114 | } |
115 | |
116 | void afterRequestImpl(AfterRequestHandler afterRequestHandler); |
117 | |
118 | template<typename Rule, typename ViewHandler, typename ViewTraits, int ... I, typename ... Args> |
119 | bool routeHelper(QtPrivate::IndexesList<I...>, Args &&... args) |
120 | { |
121 | return routeImpl<Rule, |
122 | ViewHandler, |
123 | ViewTraits, |
124 | typename VariadicTypeAt<I, Args...>::Type...>(std::forward<Args>(args)...); |
125 | } |
126 | |
127 | template<typename Rule, typename ViewHandler, typename ViewTraits, typename ... Args> |
128 | bool routeImpl(Args &&...args, ViewHandler &&viewHandler) |
129 | { |
130 | auto routerHandler = [this, viewHandler = std::forward<ViewHandler>(viewHandler)]( |
131 | const QRegularExpressionMatch &match, |
132 | const QHttpServerRequest &request, |
133 | QHttpServerResponder &&responder) { |
134 | auto boundViewHandler = router()->bindCaptured(viewHandler, match); |
135 | responseImpl<ViewTraits>(boundViewHandler, request, std::move(responder)); |
136 | }; |
137 | |
138 | auto rule = std::make_unique<Rule>(std::forward<Args>(args)..., std::move(routerHandler)); |
139 | |
140 | return router()->addRule<ViewHandler, ViewTraits>(std::move(rule)); |
141 | } |
142 | |
143 | template<typename ViewTraits, typename T> |
144 | void responseImpl(T &boundViewHandler, const QHttpServerRequest &request, |
145 | QHttpServerResponder &&responder) |
146 | { |
147 | if constexpr (ViewTraits::Arguments::PlaceholdersCount == 0) { |
148 | ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler()); |
149 | sendResponse(std::move(response), request, std::move(responder)); |
150 | } else if constexpr (ViewTraits::Arguments::PlaceholdersCount == 1) { |
151 | if constexpr (ViewTraits::Arguments::Last::IsRequest::Value) { |
152 | ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler(request)); |
153 | sendResponse(std::move(response), request, std::move(responder)); |
154 | } else { |
155 | static_assert(std::is_same_v<typename ViewTraits::ReturnType, void>, |
156 | "Handlers with responder argument must have void return type." ); |
157 | boundViewHandler(std::move(responder)); |
158 | } |
159 | } else if constexpr (ViewTraits::Arguments::PlaceholdersCount == 2) { |
160 | static_assert(std::is_same_v<typename ViewTraits::ReturnType, void>, |
161 | "Handlers with responder argument must have void return type." ); |
162 | if constexpr (ViewTraits::Arguments::Last::IsRequest::Value) { |
163 | boundViewHandler(std::move(responder), request); |
164 | } else { |
165 | boundViewHandler(request, std::move(responder)); |
166 | } |
167 | } else { |
168 | static_assert(dependent_false_v<ViewTraits>); |
169 | } |
170 | } |
171 | |
172 | bool handleRequest(const QHttpServerRequest &request, |
173 | QHttpServerResponder &responder) override final; |
174 | void missingHandler(const QHttpServerRequest &request, |
175 | QHttpServerResponder &&responder) override final; |
176 | |
177 | void sendResponse(QHttpServerResponse &&response, const QHttpServerRequest &request, |
178 | QHttpServerResponder &&responder); |
179 | |
180 | #if QT_CONFIG(future) |
181 | void sendResponse(QFuture<QHttpServerResponse> &&response, const QHttpServerRequest &request, |
182 | QHttpServerResponder &&responder); |
183 | #endif |
184 | }; |
185 | |
186 | QT_END_NAMESPACE |
187 | |
188 | #endif // QHTTPSERVER_H |
189 | |