Coverage Report

Created: 2024-09-11 06:19

/src/crow/include/crow/middleware.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include "crow/http_request.h"
4
#include "crow/http_response.h"
5
#include "crow/utility.h"
6
7
#include <tuple>
8
#include <type_traits>
9
#include <iostream>
10
#include <utility>
11
12
namespace crow // NOTE: Already documented in "crow/app.h"
13
{
14
15
    /// Local middleware should extend ILocalMiddleware
16
    struct ILocalMiddleware
17
    {
18
        using call_global = std::false_type;
19
    };
20
21
    namespace detail
22
    {
23
        template<typename MW>
24
        struct check_before_handle_arity_3_const
25
        {
26
            template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle>
27
            struct get
28
            {};
29
        };
30
31
        template<typename MW>
32
        struct check_before_handle_arity_3
33
        {
34
            template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::before_handle>
35
            struct get
36
            {};
37
        };
38
39
        template<typename MW>
40
        struct check_after_handle_arity_3_const
41
        {
42
            template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle>
43
            struct get
44
            {};
45
        };
46
47
        template<typename MW>
48
        struct check_after_handle_arity_3
49
        {
50
            template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::after_handle>
51
            struct get
52
            {};
53
        };
54
55
        template<typename MW>
56
        struct check_global_call_false
57
        {
58
            template<typename T, typename std::enable_if<T::call_global::value == false, bool>::type = true>
59
            struct get
60
            {};
61
        };
62
63
        template<typename T>
64
        struct is_before_handle_arity_3_impl
65
        {
66
            template<typename C>
67
            static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*);
68
69
            template<typename C>
70
            static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*);
71
72
            template<typename C>
73
            static std::false_type f(...);
74
75
        public:
76
            static const bool value = decltype(f<T>(nullptr))::value;
77
        };
78
79
        template<typename T>
80
        struct is_after_handle_arity_3_impl
81
        {
82
            template<typename C>
83
            static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*);
84
85
            template<typename C>
86
            static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*);
87
88
            template<typename C>
89
            static std::false_type f(...);
90
91
        public:
92
            static constexpr bool value = decltype(f<T>(nullptr))::value;
93
        };
94
95
        template<typename MW>
96
        struct is_middleware_global
97
        {
98
            template<typename C>
99
            static std::false_type f(typename check_global_call_false<MW>::template get<C>*);
100
101
            template<typename C>
102
            static std::true_type f(...);
103
104
            static const bool value = decltype(f<MW>(nullptr))::value;
105
        };
106
107
        template<typename MW, typename Context, typename ParentContext>
108
        typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type
109
          before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
110
        {
111
            mw.before_handle(req, res, ctx.template get<MW>(), ctx);
112
        }
113
114
        template<typename MW, typename Context, typename ParentContext>
115
        typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type
116
          before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
117
        {
118
            mw.before_handle(req, res, ctx.template get<MW>());
119
        }
120
121
        template<typename MW, typename Context, typename ParentContext>
122
        typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type
123
          after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
124
        {
125
            mw.after_handle(req, res, ctx.template get<MW>(), ctx);
126
        }
127
128
        template<typename MW, typename Context, typename ParentContext>
129
        typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type
130
          after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
131
        {
132
            mw.after_handle(req, res, ctx.template get<MW>());
133
        }
134
135
136
        template<typename CallCriteria,
137
                 int N, typename Context, typename Container>
138
        typename std::enable_if<(N < std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
139
          middleware_call_helper(const CallCriteria& cc, Container& middlewares, request& req, response& res, Context& ctx)
140
        {
141
142
            using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
143
144
            if (!cc.template enabled<CurrentMW>(N))
145
            {
146
                return middleware_call_helper<CallCriteria, N + 1, Context, Container>(cc, middlewares, req, res, ctx);
147
            }
148
149
            using parent_context_t = typename Context::template partial<N - 1>;
150
            before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
151
            if (res.is_completed())
152
            {
153
                after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
154
                return true;
155
            }
156
157
            if (middleware_call_helper<CallCriteria, N + 1, Context, Container>(cc, middlewares, req, res, ctx))
158
            {
159
                after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
160
                return true;
161
            }
162
163
            return false;
164
        }
165
166
        template<typename CallCriteria, int N, typename Context, typename Container>
167
        typename std::enable_if<(N >= std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
168
          middleware_call_helper(const CallCriteria& /*cc*/, Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/)
169
0
        {
170
0
            return false;
171
0
        }
172
173
        template<typename CallCriteria, int N, typename Context, typename Container>
174
        typename std::enable_if<(N < 0)>::type
175
          after_handlers_call_helper(const CallCriteria& /*cc*/, Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
176
0
        {
177
0
        }
178
179
        template<typename CallCriteria, int N, typename Context, typename Container>
180
        typename std::enable_if<(N == 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res)
181
        {
182
            using parent_context_t = typename Context::template partial<N - 1>;
183
            using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
184
            if (cc.template enabled<CurrentMW>(N))
185
            {
186
                after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
187
            }
188
        }
189
190
        template<typename CallCriteria, int N, typename Context, typename Container>
191
        typename std::enable_if<(N > 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res)
192
        {
193
            using parent_context_t = typename Context::template partial<N - 1>;
194
            using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
195
            if (cc.template enabled<CurrentMW>(N))
196
            {
197
                after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
198
            }
199
            after_handlers_call_helper<CallCriteria, N - 1, Context, Container>(cc, middlewares, ctx, req, res);
200
        }
201
202
        // A CallCriteria that accepts only global middleware
203
        struct middleware_call_criteria_only_global
204
        {
205
            template<typename MW>
206
            constexpr bool enabled(int) const
207
            {
208
                return is_middleware_global<MW>::value;
209
            }
210
        };
211
212
        template<typename F, typename... Args>
213
        typename std::enable_if<black_magic::CallHelper<F, black_magic::S<Args...>>::value, void>::type
214
          wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args)
215
        {
216
            static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
217
                          "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
218
219
            res = crow::response(f(std::forward<Args>(args)...));
220
            res.end();
221
        }
222
223
        template<typename F, typename... Args>
224
        typename std::enable_if<
225
          !black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
226
            black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value,
227
          void>::type
228
          wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
229
0
        {
230
0
            static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
231
0
                          "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
232
233
0
            res = crow::response(f(req, std::forward<Args>(args)...));
234
0
            res.end();
235
0
        }
236
237
        template<typename F, typename... Args>
238
        typename std::enable_if<
239
          !black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
240
            !black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
241
            black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value,
242
          void>::type
243
          wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args)
244
0
        {
245
0
            static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>(), std::declval<Args>()...))>::value,
246
0
                          "Handler function with response argument should have void return type");
247
248
0
            f(res, std::forward<Args>(args)...);
249
0
        }
Unexecuted instantiation: _ZN4crow6detail20wrapped_handler_callIZNS_4CrowIJEE13add_blueprintEvEUlRNS_8responseENSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEEE_JSC_EEENS6_9enable_ifIXaaaantsr11black_magic10CallHelperIT_NS_11black_magic1SIJDpT0_EEEEE5valuentsr11black_magic10CallHelperISF_NSH_IJRNS_7requestESJ_EEEEE5valuesr11black_magic10CallHelperISF_NSH_IJS5_SJ_EEEEE5valueEvE4typeESM_S5_RKSF_DpOSI_
Unexecuted instantiation: _ZN4crow6detail20wrapped_handler_callIZNS_4CrowIJEE14add_static_dirEvEUlRNS_8responseENSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEEE_JSC_EEENS6_9enable_ifIXaaaantsr11black_magic10CallHelperIT_NS_11black_magic1SIJDpT0_EEEEE5valuentsr11black_magic10CallHelperISF_NSH_IJRNS_7requestESJ_EEEEE5valuesr11black_magic10CallHelperISF_NSH_IJS5_SJ_EEEEE5valueEvE4typeESM_S5_RKSF_DpOSI_
250
251
        template<typename F, typename... Args>
252
        typename std::enable_if<
253
          !black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
254
            !black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
255
            !black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value &&
256
            black_magic::CallHelper<F, black_magic::S<const crow::request&, crow::response&, Args...>>::value,
257
          void>::type
258
          wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
259
        {
260
            static_assert(std::is_same<void, decltype(f(std::declval<crow::request&>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
261
                          "Handler function with response argument should have void return type");
262
263
            f(req, res, std::forward<Args>(args)...);
264
        }
265
266
        // wrapped_handler_call transparently wraps a handler call behind (req, res, args...)
267
        template<typename F, typename... Args>
268
        typename std::enable_if<
269
          !black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
270
            !black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
271
            !black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value &&
272
            !black_magic::CallHelper<F, black_magic::S<const crow::request&, crow::response&, Args...>>::value,
273
          void>::type
274
          wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
275
        {
276
            static_assert(std::is_same<void, decltype(f(std::declval<crow::request&>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
277
                          "Handler function with response argument should have void return type");
278
279
            f(req, res, std::forward<Args>(args)...);
280
        }
281
282
        template<bool Reversed>
283
        struct middleware_call_criteria_dynamic
284
        {};
285
286
        template<>
287
        struct middleware_call_criteria_dynamic<false>
288
        {
289
            middleware_call_criteria_dynamic(const std::vector<int>& indices):
290
0
              indices(indices), slider(0) {}
291
292
            template<typename>
293
            bool enabled(int mw_index) const
294
            {
295
                if (slider < int(indices.size()) && indices[slider] == mw_index)
296
                {
297
                    slider++;
298
                    return true;
299
                }
300
                return false;
301
            }
302
303
        private:
304
            const std::vector<int>& indices;
305
            mutable int slider;
306
        };
307
308
        template<>
309
        struct middleware_call_criteria_dynamic<true>
310
        {
311
            middleware_call_criteria_dynamic(const std::vector<int>& indices):
312
0
              indices(indices), slider(int(indices.size()) - 1) {}
313
314
            template<typename>
315
            bool enabled(int mw_index) const
316
            {
317
                if (slider >= 0 && indices[slider] == mw_index)
318
                {
319
                    slider--;
320
                    return true;
321
                }
322
                return false;
323
            }
324
325
        private:
326
            const std::vector<int>& indices;
327
            mutable int slider;
328
        };
329
330
    } // namespace detail
331
} // namespace crow