LCOV - code coverage report
Current view: top level - extra/webserver/webserver/request - SPWebRequest.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 211 303 69.6 %
Date: 2024-05-12 00:16:13 Functions: 57 88 64.8 %

          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             : }

Generated by: LCOV version 1.14