Line data Source code
1 : /**
2 : Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #ifndef EXTRA_WEBSERVER_WEBSERVER_REQUEST_SPWEBREQUESTHANDLER_H_
24 : #define EXTRA_WEBSERVER_WEBSERVER_REQUEST_SPWEBREQUESTHANDLER_H_
25 :
26 : #include "SPWebRequest.h"
27 :
28 : namespace STAPPLER_VERSIONIZED stappler::web {
29 :
30 : class InputFilter;
31 :
32 : class RequestHandler : public AllocBase {
33 : public:
34 : using HandlerCallback = Function<RequestHandler *()>;
35 :
36 : template <typename T, typename ... Args>
37 175 : static HandlerCallback Make(Args && ... args) {
38 800 : return HandlerCallback([=] {
39 1600 : return new T(std::forward<Args>(args)...);
40 175 : });
41 : }
42 :
43 0 : virtual ~RequestHandler() { }
44 :
45 0 : virtual bool isRequestPermitted(Request &) { return false; }
46 :
47 : /**
48 : * https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
49 : *
50 : * @param rctx - new request, avoid to modify it
51 : * @param origin - actual value of Origin header, it's sender's domain name
52 : * @param isPreflight - we recieve preflight request (so, method and headers should be filled)
53 : * @param method - method for preflighted request
54 : * @param headers - extra (X-*) headers for preflighted request
55 : * @return for forbidden CORS requests server will return "405 Method Not Allowed"
56 : */
57 0 : virtual bool isCorsPermitted(Request &, const StringView &origin, bool isPreflight = false,
58 0 : const StringView &method = "", const StringView &headers = "") { return true; }
59 :
60 : /**
61 : * Available method for CORS preflighted requests
62 : */
63 0 : virtual StringView getCorsAllowMethods(Request &) {
64 0 : return "GET, HEAD, POST, PUT, DELETE, OPTIONS";
65 : }
66 :
67 : /**
68 : * Available extra headers for CORS preflighted requests
69 : */
70 0 : virtual StringView getCorsAllowHeaders(Request &) {
71 0 : return StringView();
72 : }
73 :
74 : /**
75 : * Caching time for preflight response
76 : */
77 0 : virtual StringView getCorsMaxAge(Request &) {
78 0 : return "1728000"; // 20 days
79 : }
80 :
81 : /** Be sure to call supermethod when overload this method! */
82 : virtual Status onRequestRecieved(Request &, StringView origin, StringView path, const Value &);
83 :
84 : virtual Status onPostReadRequest(Request &);
85 : virtual Status onTranslateName(Request &);
86 : virtual Status onQuickHandler(Request &, int v);
87 : virtual void onInsertFilter(Request &);
88 : virtual Status onHandler(Request &);
89 :
90 : virtual void onFilterInit(InputFilter *f);
91 : virtual void onFilterUpdate(InputFilter *f);
92 : virtual void onFilterComplete(InputFilter *f);
93 :
94 : virtual const Value &getOptions() const;
95 :
96 : void setAccessRole(db::AccessRoleId role);
97 : db::AccessRoleId getAccessRole() const;
98 :
99 : protected:
100 : Request _request;
101 : StringView _originPath;
102 : StringView _subPath;
103 : Vector<StringView> _subPathVec;
104 : Value _options;
105 : db::AccessRoleId _accessRole = db::AccessRoleId::Nobody;
106 : db::Transaction _transaction = nullptr;
107 : };
108 :
109 : class DefaultHandler : public RequestHandler {
110 : public:
111 : virtual bool isRequestPermitted(Request &) override { return true; }
112 : };
113 :
114 : class DataHandler : public RequestHandler {
115 : public:
116 : enum class AllowMethod : uint8_t {
117 : None = 0,
118 : Get = 1 << 0,
119 : Post = 1 << 1,
120 : Put = 1 << 2,
121 : Delete = 1 << 3,
122 : All = 0xFF,
123 : };
124 :
125 0 : virtual ~DataHandler() { }
126 :
127 : // overload point
128 0 : virtual bool processDataHandler(Request &req, Value &result, Value &input) { return false; }
129 :
130 : virtual Status onTranslateName(Request &) override;
131 : virtual void onInsertFilter(Request &) override;
132 : virtual Status onHandler(Request &) override;
133 :
134 : virtual void onFilterComplete(InputFilter *f) override;
135 :
136 : protected:
137 150 : virtual bool allowJsonP() { return true; }
138 :
139 : Status writeResult(Value &);
140 :
141 : AllowMethod _allow = AllowMethod::All;
142 : db::InputConfig _config = db::InputConfig({
143 : db::InputConfig::Require::Data | db::InputConfig::Require::FilesAsData,
144 : 0,
145 : 256,
146 : 0
147 : });
148 : InputFilter *_filter;
149 : };
150 :
151 : SP_DEFINE_ENUM_AS_MASK(DataHandler::AllowMethod)
152 :
153 : class FilesystemHandler : public RequestHandler {
154 : public:
155 : FilesystemHandler(const String &path, size_t cacheTimeInSeconds = stappler::maxOf<size_t>());
156 : FilesystemHandler(const String &path, const String &ct, size_t cacheTimeInSeconds = stappler::maxOf<size_t>());
157 :
158 : virtual bool isRequestPermitted(Request &) override;
159 : virtual Status onTranslateName(Request &) override;
160 :
161 : protected:
162 : String _path;
163 : String _contentType;
164 : size_t _cacheTime;
165 : };
166 :
167 : class RequestHandlerMap : public AllocBase {
168 : public:
169 : class HandlerInfo;
170 : class Handler;
171 :
172 : class Handler : public RequestHandler {
173 : public: // simplified interface
174 : template <typename T, typename ... Args>
175 100 : static Function<Handler *()> Make(Args && ... args) {
176 100 : return Function<Handler *()>([=] {
177 200 : return new T(std::forward<Args>(args)...);
178 100 : });
179 : }
180 :
181 : virtual bool isPermitted();
182 : virtual Status onRequest();
183 : virtual Value onData();
184 :
185 : public:
186 : Handler();
187 : virtual ~Handler();
188 :
189 : virtual void onParams(const HandlerInfo *, Value &&);
190 100 : virtual bool isRequestPermitted(Request &) override { return isPermitted(); }
191 : virtual Status onTranslateName(Request &) override;
192 : virtual void onInsertFilter(Request &) override;
193 : virtual Status onHandler(Request &) override;
194 :
195 : virtual void onFilterComplete(InputFilter *f) override;
196 :
197 : const Request &getRequest() const { return _request; }
198 : const Value &getParams() const { return _params; }
199 : const Value &getQueryFields() const { return _queryFields; }
200 : const Value &getInputFields() const { return _inputFields; }
201 :
202 : protected:
203 100 : virtual bool allowJsonP() { return true; }
204 :
205 : bool processQueryFields(Value &&);
206 : bool processInputFields(InputFilter *);
207 :
208 : Status writeResult(Value &);
209 :
210 : db::InputFile *getInputFile(const StringView &);
211 :
212 : const HandlerInfo *_info = nullptr;
213 : InputFilter *_filter = nullptr;
214 :
215 : Value _params; // query path params
216 : Value _queryFields;
217 : Value _inputFields;
218 : };
219 :
220 : class HandlerInfo : public AllocBase {
221 : public:
222 : HandlerInfo(const StringView &name, RequestMethod, const StringView &pattern,
223 : Function<Handler *()> &&, Value && = Value());
224 :
225 : HandlerInfo &addQueryFields(std::initializer_list<db::Field> il);
226 : HandlerInfo &addQueryFields(Vector<db::Field> &&il);
227 :
228 : HandlerInfo &addInputFields(std::initializer_list<db::Field> il);
229 : HandlerInfo &addInputFields(Vector<db::Field> &&il);
230 : HandlerInfo &setInputConfig(db::InputConfig);
231 :
232 : Value match(const StringView &path, size_t &score) const;
233 :
234 : Handler *onHandler(Value &&) const;
235 :
236 : RequestMethod getMethod() const;
237 : const db::InputConfig &getInputConfig() const;
238 :
239 : StringView getName() const;
240 : StringView getPattern() const;
241 : const Value &getOptions() const;
242 :
243 : const db::Scheme &getQueryScheme() const;
244 : const db::Scheme &getInputScheme() const;
245 :
246 : protected:
247 : struct Fragment {
248 : enum Type : uint16_t {
249 : Text,
250 : Pattern,
251 : };
252 :
253 250 : Fragment(Type t, StringView s) : type(t), string(s.str<Interface>()) { }
254 :
255 : Type type;
256 : String string;
257 : };
258 :
259 : String name;
260 : RequestMethod method = RequestMethod::Get;
261 : String pattern;
262 : Function<Handler *()> handler;
263 : Value options;
264 :
265 : db::Scheme queryFields;
266 : db::Scheme inputFields;
267 : Vector<Fragment> fragments;
268 : };
269 :
270 : RequestHandlerMap();
271 : virtual ~RequestHandlerMap();
272 :
273 : RequestHandlerMap(RequestHandlerMap &&) = default;
274 : RequestHandlerMap &operator=(RequestHandlerMap &&) = default;
275 :
276 : RequestHandlerMap(const RequestHandlerMap &) = delete;
277 : RequestHandlerMap &operator=(const RequestHandlerMap &) = delete;
278 :
279 : Handler *onRequest(Request &req, const StringView &path) const;
280 :
281 : HandlerInfo &addHandler(const StringView &name, RequestMethod, const StringView &pattern,
282 : Function<Handler *()> &&, Value && = Value());
283 :
284 : HandlerInfo &addHandler(const StringView &name, RequestMethod, const StringView &pattern,
285 : Function<bool(Handler &)> &&, Function<Value(Handler &)> &&, Value && = Value());
286 :
287 : const Vector<HandlerInfo> &getHandlers() const;
288 :
289 : protected:
290 : Vector<HandlerInfo> _handlers;
291 : };
292 :
293 : }
294 :
295 : #endif /* EXTRA_WEBSERVER_WEBSERVER_REQUEST_SPWEBREQUESTHANDLER_H_ */
|