Line data Source code
1 : #ifndef THINGER_HTTP_ROUTE_DESCRIPTOR_HPP
2 : #define THINGER_HTTP_ROUTE_DESCRIPTOR_HPP
3 :
4 : #include <functional>
5 : #include <regex>
6 : #include <string>
7 : #include <vector>
8 : #include <memory>
9 : #include "../request.hpp"
10 : #include "../../../util/types.hpp"
11 :
12 : #ifdef THINGER_HTTP_VALIJSON_ENABLED
13 : #include <valijson/schema.hpp>
14 : #include <valijson/schema_parser.hpp>
15 : #include <valijson/validator.hpp>
16 : #include <valijson/validation_results.hpp>
17 : #include <valijson/adapters/nlohmann_json_adapter.hpp>
18 : #endif
19 :
20 : namespace thinger::http {
21 :
22 : // Route parameters syntax:
23 : // 1. Simple parameters: :param_name
24 : // Example: "/api/v1/users/:user/devices/:device"
25 : // Matches any non-slash characters
26 : //
27 : // 2. Parameters with custom regex: :param_name(regex)
28 : // Example: "/api/v1/users/:id([0-9]+)" - numeric ID only
29 : // Example: "/api/v1/users/:user([a-zA-Z0-9_-]{1,32})" - alphanumeric with length limit
30 : // Example: "/files/:path(.+)" - match everything including slashes
31 : //
32 : // Common patterns:
33 : #define ID_PATTERN "[0-9]+" // Numeric ID
34 : #define ALPHANUM_ID "[a-zA-Z0-9_-]{1,32}" // Alphanumeric ID (1-32 chars)
35 : #define UUID_PATTERN "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
36 : #define EMAIL_PATTERN "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"
37 : #define SLUG_PATTERN "[a-z0-9]+(?:-[a-z0-9]+)*" // URL-friendly slug
38 :
39 : // Authorization levels
40 : enum class auth_level {
41 : PUBLIC, // No authentication required
42 : USER, // Valid user required
43 : ADMIN // Admin user required
44 : };
45 :
46 : // Forward declarations
47 : class request;
48 : class response;
49 :
50 : // Callback types for route handlers - supporting multiple signatures
51 : using route_callback_response_only = std::function<void(response&)>;
52 : using route_callback_json_response = std::function<void(nlohmann::json&, response&)>;
53 : using route_callback_request_response = std::function<void(request&, response&)>;
54 : using route_callback_request_json_response = std::function<void(request&, nlohmann::json&, response&)>;
55 : using route_callback_awaitable = std::function<thinger::awaitable<void>(request&, response&)>;
56 :
57 : // Legacy callback types (for backward compatibility if needed)
58 : using route_callback = route_callback_request_response;
59 : using route_callback_json = route_callback_request_json_response;
60 :
61 : class route {
62 : public:
63 : route(const std::string& pattern);
64 :
65 : // Set the callback handler - multiple signatures supported
66 : route& operator=(route_callback_response_only callback);
67 : route& operator=(route_callback_json_response callback);
68 : route& operator=(route_callback_request_response callback);
69 : route& operator=(route_callback_request_json_response callback);
70 : route& operator=(route_callback_awaitable callback);
71 :
72 : // Deferred body mode - handler reads body at its discretion
73 : route& deferred_body(bool enabled = true);
74 1023 : bool is_deferred_body() const { return deferred_body_; }
75 :
76 : // Set JSON Schema for request body validation
77 : route& schema(const nlohmann::json& json_schema);
78 :
79 : // Set authorization level
80 : route& auth(auth_level level);
81 :
82 : // Set description for API documentation
83 : route& description(const std::string& desc);
84 :
85 : // Check if route matches the given path
86 : bool matches(const std::string& path, std::smatch& matches) const;
87 :
88 : // Get route parameters from regex
89 1585 : const std::vector<std::string>& get_parameters() const { return parameters_; }
90 :
91 : // Get authorization level
92 1031 : auth_level get_auth_level() const { return auth_level_; }
93 :
94 : // Handle the request (synchronous)
95 : void handle_request(request& req, response& res) const;
96 :
97 : // Handle the request (coroutine — works for all callback types)
98 : thinger::awaitable<void> handle_request_coro(request& req, response& res) const;
99 :
100 : // Get the original pattern
101 1028 : const std::string& get_pattern() const { return pattern_; }
102 :
103 : private:
104 : std::string pattern_;
105 : std::regex regex_;
106 : std::vector<std::string> parameters_;
107 : auth_level auth_level_ = auth_level::PUBLIC;
108 : std::string description_;
109 : bool deferred_body_ = false;
110 : std::variant<
111 : route_callback_response_only,
112 : route_callback_json_response,
113 : route_callback_request_response,
114 : route_callback_request_json_response,
115 : route_callback_awaitable
116 : > callback_;
117 :
118 : #ifdef THINGER_HTTP_VALIJSON_ENABLED
119 : nlohmann::json json_schema_;
120 : std::shared_ptr<valijson::Schema> schema_;
121 :
122 : bool validate_json(const nlohmann::json& json, response& res) const;
123 : #endif
124 :
125 : void parse_parameters();
126 : };
127 :
128 : } // namespace thinger::http
129 :
130 : #endif // THINGER_HTTP_ROUTE_DESCRIPTOR_HPP
|