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 : #include "SPWebRequest.h"
24 :
25 : #include "SPDbAdapter.h"
26 : #include "SPWebRequestController.h"
27 : #include "SPWebOutput.h"
28 : #include "SPWebSession.h"
29 : #include "SPWebRoot.h"
30 : #include "SPDbUser.h"
31 :
32 : namespace STAPPLER_VERSIONIZED stappler::web {
33 :
34 14975 : static RequestController *getRequestFromContext(pool_t *p, uint32_t tag, const void *ptr) {
35 14975 : switch (tag) {
36 6075 : case uint32_t(config::TAG_REQUEST): return (RequestController *)ptr; break;
37 : }
38 8900 : return nullptr;
39 : }
40 :
41 7775 : Request Request::getCurrent() {
42 7775 : RequestController *ret = nullptr;
43 7775 : pool::foreach_info(&ret, [] (void *ud, pool_t *p, uint32_t tag, const void *data) -> bool {
44 14975 : auto ptr = getRequestFromContext(p, tag, data);
45 14975 : if (ptr) {
46 6075 : *((RequestController **)ud) = ptr;
47 6075 : return false;
48 : }
49 8900 : return true;
50 : });
51 :
52 15550 : return Request(ret);
53 : }
54 :
55 3700 : Request::Request() : _buffer(nullptr), _config(nullptr) { }
56 :
57 14975 : Request::Request(RequestController *cfg) : _buffer(cfg), _config(cfg) {
58 14975 : this->init(&_buffer);
59 14975 : }
60 :
61 0 : Request & Request::operator =(RequestController *cfg) {
62 0 : _buffer = Buffer(cfg);
63 0 : _config = cfg;
64 0 : this->init(&_buffer);
65 0 : return *this;
66 : }
67 :
68 0 : Request::Request(Request &&other) : _buffer(other._config), _config(other._config) {
69 0 : this->init(&_buffer);
70 0 : }
71 0 : Request & Request::operator =(Request &&other) {
72 0 : _buffer = Buffer(other._config);
73 0 : _config = other._config;
74 0 : this->init(&_buffer);
75 0 : return *this;
76 : }
77 :
78 18125 : Request::Request(const Request &other) :_buffer(other._config), _config(other._config) {
79 18125 : this->init(&_buffer);
80 18125 : }
81 :
82 3700 : Request & Request::operator =(const Request &other) {
83 3700 : _buffer = Buffer(other._config);
84 3700 : _config = other._config;
85 3700 : this->init(&_buffer);
86 3700 : return *this;
87 : }
88 :
89 28250 : const RequestInfo &Request::getInfo() const {
90 28250 : return _config->getInfo();
91 : }
92 :
93 16600 : StringView Request::getRequestHeader(StringView key) const {
94 16600 : return _config->getRequestHeader(key);
95 : }
96 :
97 50 : void Request::foreachRequestHeaders(const Callback<void(StringView, StringView)> &cb) const {
98 50 : _config->foreachRequestHeaders(cb);
99 50 : }
100 :
101 825 : StringView Request::getResponseHeader(StringView key) const {
102 825 : return _config->getResponseHeader(key);
103 : }
104 :
105 0 : void Request::foreachResponseHeaders(const Callback<void(StringView, StringView)> &cb) const {
106 0 : _config->foreachResponseHeaders(cb);
107 0 : }
108 :
109 2350 : void Request::setResponseHeader(StringView key, StringView value) const {
110 2350 : _config->setResponseHeader(key, value);
111 2350 : }
112 :
113 25 : void Request::clearResponseHeaders() const {
114 25 : _config->clearResponseHeaders();
115 25 : }
116 :
117 0 : StringView Request::getErrorHeader(StringView key) const {
118 0 : return _config->getErrorHeader(key);
119 : }
120 :
121 0 : void Request::foreachErrorHeaders(const Callback<void(StringView, StringView)> &cb) const {
122 0 : _config->foreachErrorHeaders(cb);
123 0 : }
124 :
125 0 : void Request::setErrorHeader(StringView key, StringView value) const {
126 0 : _config->setErrorHeader(key, value);
127 0 : }
128 :
129 0 : void Request::clearErrorHeaders() const {
130 0 : _config->clearErrorHeaders();
131 0 : }
132 :
133 40500 : Request::Buffer::Buffer(RequestController *cfg) : _config(cfg) { }
134 0 : Request::Buffer::Buffer(Buffer &&other) : _config(other._config) { }
135 3700 : Request::Buffer& Request::Buffer::operator=(Buffer &&other) { _config = other._config; return *this; }
136 :
137 0 : Request::Buffer::Buffer(const Buffer &other) : _config(other._config) { }
138 0 : Request::Buffer& Request::Buffer::operator=(const Buffer &other) { _config = other._config; return *this; }
139 :
140 0 : Request::Buffer::int_type Request::Buffer::overflow(int_type c) {
141 0 : _config->putc(c);
142 0 : return c;
143 : }
144 :
145 0 : Request::Buffer::pos_type Request::Buffer::seekoff(off_type off, ios_base::seekdir way, ios_base::openmode) {
146 0 : return _config->getBytesSent();
147 : }
148 :
149 0 : Request::Buffer::pos_type Request::Buffer::seekpos(pos_type pos, ios_base::openmode mode) {
150 0 : return _config->getBytesSent();
151 : }
152 :
153 0 : int Request::Buffer::sync() {
154 0 : _config->flush();
155 0 : return 0;
156 : }
157 :
158 1221125 : Request::Buffer::streamsize Request::Buffer::xsputn(const char_type* s, streamsize n) {
159 1221125 : return _config->write((const uint8_t *)s, n);
160 : }
161 :
162 3050 : void Request::setRequestHandler(RequestHandler *h) {
163 3050 : _config->_handler = h;
164 3050 : }
165 11000 : RequestHandler *Request::getRequestHandler() const {
166 11000 : return _config->_handler;
167 : }
168 :
169 2150 : void Request::writeData(const Value &data, bool allowJsonP) {
170 2150 : output::writeData(*this, data, allowJsonP);
171 2150 : }
172 :
173 : /* request params setters */
174 0 : void Request::setDocumentRoot(StringView str) {
175 0 : _config->setDocumentRoot(str);
176 0 : }
177 :
178 2300 : void Request::setContentType(StringView str) {
179 2300 : _config->setContentType(str);
180 2300 : }
181 :
182 0 : void Request::setHandler(StringView str) {
183 0 : _config->setHandler(str);
184 0 : }
185 :
186 0 : void Request::setContentEncoding(StringView str) {
187 0 : _config->setContentEncoding(str);
188 0 : }
189 :
190 100 : void Request::setFilename(StringView str, bool updateStat, Time mtime) {
191 100 : _config->setFilename(str, updateStat, mtime);
192 100 : }
193 :
194 50 : void Request::setCookie(StringView name, StringView value, TimeInterval maxAge, CookieFlags flags) {
195 50 : _config->_cookies.emplace(name.pdup(pool()), CookieStorageInfo{value.str<Interface>(), flags, maxAge});
196 50 : }
197 :
198 25 : void Request::removeCookie(StringView name, CookieFlags flags) {
199 25 : _config->_cookies.emplace(name.pdup(pool()), CookieStorageInfo{String(), flags, TimeInterval::seconds(0)});
200 25 : }
201 :
202 0 : const Map<StringView, CookieStorageInfo> Request::getResponseCookies() const {
203 0 : return _config->_cookies;
204 : }
205 :
206 150 : StringView Request::getCookie(StringView name, bool removeFromHeadersTable) const {
207 150 : return _config->getCookie(name, removeFromHeadersTable);
208 : }
209 :
210 25 : Session *Request::authorizeUser(db::User *user, TimeInterval maxAge) {
211 25 : if (_config->_session) {
212 0 : _config->_session->cancel();
213 : }
214 25 : auto s = new Session(*this, user, maxAge);
215 25 : if (s->isValid()) {
216 25 : _config->_session = s;
217 25 : _config->_user = user;
218 25 : return s;
219 : }
220 0 : return nullptr;
221 : }
222 :
223 650 : void Request::setInputFilter(InputFilter *filter) {
224 650 : _config->setInputFilter(filter);
225 650 : }
226 :
227 775 : InputFilter *Request::getInputFilter() const {
228 775 : return _config->_filter;
229 : }
230 :
231 525 : void Request::setUser(db::User *u) {
232 525 : if (u) {
233 525 : _config->_user = u;
234 525 : if (_config->_user->isAdmin()) {
235 525 : _config->_accessRole = std::max(db::AccessRoleId::Admin, _config->_accessRole);
236 : } else {
237 0 : _config->_accessRole = std::max(db::AccessRoleId::Authorized, _config->_accessRole);
238 : }
239 525 : _config->_userId = u->getObjectId();
240 : }
241 525 : }
242 :
243 0 : void Request::setUser(int64_t id) {
244 0 : _config->_userId = id;
245 0 : }
246 :
247 150 : Session *Request::getSession() {
248 150 : if (!_config->_session) {
249 100 : _config->_session = new Session(*this);
250 : }
251 :
252 150 : if (_config->_session->isValid()) {
253 125 : return _config->_session;
254 : }
255 25 : return nullptr;
256 : }
257 :
258 100 : db::User *Request::getUser() {
259 100 : if (!_config->_user) {
260 100 : if (auto s = getSession()) {
261 75 : _config->_user = s->getUser();
262 75 : if (_config->_user && _config->_user->isAdmin()) {
263 75 : setAccessRole(db::AccessRoleId::Admin);
264 : }
265 : }
266 : }
267 100 : return _config->_user;
268 : }
269 :
270 4775 : db::User *Request::getAuthorizedUser() const {
271 4775 : return _config->_user;
272 : }
273 :
274 1650 : int64_t Request::getUserId() const {
275 1650 : return _config->_userId;
276 : }
277 :
278 550 : void Request::setStatus(Status status, StringView str) {
279 550 : _config->setStatus(status, str);
280 550 : }
281 :
282 6925 : const db::InputConfig & Request::getInputConfig() const {
283 6925 : return _config->_inputConfig;
284 : }
285 :
286 650 : void Request::setInputConfig(const db::InputConfig &cfg) {
287 650 : _config->_inputConfig = cfg;
288 650 : }
289 :
290 50 : void Request::storeObject(void *ptr, const StringView &key, Function<void()> &&cb) const {
291 50 : pool::store(pool(), ptr, key, std::move(cb));
292 50 : }
293 :
294 1250 : bool Request::performWithStorage(const Callback<bool(const db::Transaction &)> &cb) const {
295 1250 : auto ad = _config->acquireDatabase();
296 2500 : return ad.performWithTransaction([&, this] (const db::Transaction &t) {
297 1250 : t.setRole(_config->_accessRole);
298 1250 : return cb(t);
299 2500 : });
300 : }
301 :
302 800 : bool Request::isSecureConnection() const {
303 800 : return _config->isSecureConnection();
304 : }
305 :
306 12875 : RequestController *Request::config() const {
307 12875 : return _config;
308 : }
309 :
310 8400 : Host Request::host() const {
311 8400 : return Host(_config->getHost());
312 : }
313 :
314 29625 : pool_t *Request::pool() const {
315 29625 : return _config->getPool();
316 : }
317 :
318 2150 : const Vector<Value> & Request::getDebugMessages() const {
319 2150 : return _config->_debug;
320 : }
321 :
322 2150 : const Vector<Value> & Request::getErrorMessages() const {
323 2150 : return _config->_errors;
324 : }
325 :
326 50 : void Request::addErrorMessage(Value &&val) const {
327 50 : if (_config) {
328 50 : _config->_host->getRoot()->pushErrorMessage(std::move(val));
329 : }
330 50 : }
331 :
332 25 : void Request::addDebugMessage(Value &&val) const {
333 25 : if (_config) {
334 25 : _config->_host->getRoot()->pushDebugMessage(std::move(val));
335 : }
336 25 : }
337 :
338 0 : void Request::addCleanup(Function<void()> &&cb) const {
339 0 : pool::cleanup_register(pool(), std::move(cb));
340 0 : }
341 :
342 0 : bool Request::isAdministrative() {
343 0 : return getAccessRole() == db::AccessRoleId::Admin;
344 : }
345 :
346 2875 : db::AccessRoleId Request::getAccessRole() const {
347 2875 : if (_config->_accessRole == db::AccessRoleId::Nobody) {
348 2850 : if (_config->_user) {
349 0 : if (_config->_user->isAdmin()) {
350 0 : _config->_accessRole = db::AccessRoleId::Admin;
351 : } else {
352 0 : _config->_accessRole = db::AccessRoleId::Authorized;
353 : }
354 : } else {
355 2850 : Session s(*this, true);
356 2850 : if (s.isValid()) {
357 75 : auto u = s.getUser();
358 75 : if (u) {
359 75 : if (u->isAdmin()) {
360 75 : _config->_accessRole = db::AccessRoleId::Admin;
361 : } else {
362 0 : _config->_accessRole = db::AccessRoleId::Authorized;
363 : }
364 : }
365 : }
366 : #ifdef DEBUG
367 2850 : auto userIp = _config->_info.useragentIp;
368 2850 : if ((strncmp(userIp.data(), "127.", 4) == 0 || userIp == "::1") && _config->_info.queryData.getBool("admin")) {
369 0 : _config->_accessRole = db::AccessRoleId::Admin;
370 : }
371 : #endif
372 2850 : }
373 2850 : if (auto t = db::Transaction::acquireIfExists(pool())) {
374 2850 : auto role = t.getRole();
375 2850 : if (role != db::AccessRoleId::System && toInt(t.getRole()) > toInt(_config->_accessRole)) {
376 0 : _config->_accessRole = role;
377 : }
378 : }
379 : }
380 2875 : return _config->_accessRole;
381 : }
382 :
383 75 : void Request::setAccessRole(db::AccessRoleId role) const {
384 75 : _config->_accessRole = role;
385 75 : if (auto t = db::Transaction::acquireIfExists(pool())) {
386 0 : t.setRole(role);
387 : }
388 75 : }
389 :
390 350 : db::Transaction Request::acquireDbTransaction() const {
391 350 : return db::Transaction::acquire(_config->acquireDatabase());
392 : }
393 :
394 175 : Status Request::redirectTo(StringView location) {
395 175 : setResponseHeader("Location", location);
396 175 : return HTTP_SEE_OTHER;
397 : }
398 :
399 0 : Status Request::sendFile(StringView file, size_t cacheTime) {
400 0 : setFilename(filesystem::writablePath<Interface>(file), true);
401 0 : if (cacheTime == 0) {
402 0 : setResponseHeader("Cache-Control", "no-cache, must-revalidate");
403 0 : } else if (cacheTime < SIZE_MAX) {
404 0 : setResponseHeader("Cache-Control", toString("max-age=", cacheTime, ", must-revalidate", cacheTime));
405 : }
406 0 : return OK;
407 : }
408 :
409 0 : Status Request::sendFile(StringView file, StringView contentType, size_t cacheTime) {
410 0 : if (!contentType.empty()) {
411 0 : setContentType(std::move(contentType));
412 : }
413 0 : return sendFile(std::move(file), cacheTime);
414 : }
415 :
416 350 : String Request::getFullHostname(int port) const {
417 350 : auto secure = _config->isSecureConnection();
418 350 : auto &info = _config->getInfo();
419 350 : if (port == -1) {
420 350 : if (!info.url.port.empty()) {
421 350 : port = StringView(_config->getInfo().url.port).readInteger(10).get(secure ? 443 : 80);
422 : } else {
423 0 : port = secure ? 443 : 80;
424 : }
425 : }
426 :
427 350 : StringStream ret;
428 350 : ret << (secure?"https":"http") << "://" << info.url.host;
429 350 : if (port && ((secure && port != 443) || (!secure && port != 80))) {
430 350 : ret << ':' << port;
431 : }
432 :
433 700 : return ret.str();
434 350 : }
435 :
436 0 : bool Request::checkCacheHeaders(Time t, const StringView &etag) {
437 0 : return output::checkCacheHeaders(*this, t, etag);
438 : }
439 :
440 0 : bool Request::checkCacheHeaders(Time t, uint32_t idHash) {
441 0 : return output::checkCacheHeaders(*this, t, idHash);
442 : }
443 :
444 350 : Status Request::runPug(const StringView & path, const Function<bool(pug::Context &, const pug::Template &)> &cb) {
445 350 : auto cache = host().getPugCache();
446 350 : if (cache->runTemplate(path, [&, this] (pug::Context &ctx, const pug::Template &tpl) -> bool {
447 350 : initScriptContext(ctx);
448 :
449 350 : if (cb(ctx, tpl)) {
450 350 : auto lm = getResponseHeader("Last-Modified");
451 350 : auto etag = getResponseHeader("ETag");
452 350 : setResponseHeader("Content-Type", "text/html; charset=UTF-8");
453 350 : if (lm.empty() && etag.empty()) {
454 350 : setResponseHeader("Cache-Control", "no-cache, no-store, must-revalidate");
455 350 : setResponseHeader("Pragma", "no-cache");
456 350 : setResponseHeader("Expires", Time::seconds(0).toHttp<Interface>());
457 : }
458 350 : return true;
459 : }
460 0 : return false;
461 148575 : }, [&] (StringView str) { *this << str; })) {
462 350 : return DONE;
463 : }
464 0 : return HTTP_INTERNAL_SERVER_ERROR;
465 : }
466 :
467 350 : void Request::initScriptContext(pug::Context &ctx) {
468 350 : auto &info = getInfo();
469 350 : pug::VarClass serenityClass;
470 350 : serenityClass.staticFunctions.emplace("prettify", [] (pug::VarStorage &, pug::Var *var, size_t argc) -> pug::Var {
471 75 : if (var && argc == 1) {
472 150 : return pug::Var(Value(data::toString(var->readValue(), true)));
473 : }
474 0 : return pug::Var();
475 : });
476 350 : serenityClass.staticFunctions.emplace("timeToHttp", [] (pug::VarStorage &, pug::Var *var, size_t argc) -> pug::Var {
477 75 : if (var && argc == 1 && var->readValue().isInteger()) {
478 150 : return pug::Var(Value(Time::microseconds(var->readValue().asInteger()).toHttp<Interface>()));
479 : }
480 0 : return pug::Var();
481 : });
482 350 : serenityClass.staticFunctions.emplace("uuidToString", [] (pug::VarStorage &, pug::Var *var, size_t argc) -> pug::Var {
483 0 : if (var && argc == 1 && var->readValue().isBytes()) {
484 0 : return pug::Var(Value(memory::uuid(var->readValue().getBytes()).str()));
485 : }
486 0 : return pug::Var();
487 : });
488 350 : ctx.set("serenity", std::move(serenityClass));
489 1050 : ctx.set("window", Value{
490 3500 : pair("location", Value({
491 700 : pair("href", Value(toString(getFullHostname(), info.unparserUri))),
492 700 : pair("hostname", Value(info.url.host)),
493 700 : pair("pathname", Value(info.url.path)),
494 700 : pair("protocol", Value(_config->isSecureConnection() ? "https:" : "http:")),
495 1750 : }))
496 700 : });
497 350 : }
498 :
499 : }
|