LCOV - code coverage report
Current view: top level - extra/webserver/webserver/request - SPWebRequestHandler.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 223 363 61.4 %
Date: 2024-05-12 00:16:13 Functions: 39 65 60.0 %

          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 "SPWebRequestHandler.h"
      24             : #include "SPWebInputFilter.h"
      25             : #include "SPWebHost.h"
      26             : #include "SPWebRoot.h"
      27             : #include "SPDbFile.h"
      28             : 
      29             : namespace STAPPLER_VERSIONIZED stappler::web {
      30             : 
      31        3050 : Status RequestHandler::onRequestRecieved(Request & rctx, StringView originPath, StringView path, const Value &data) {
      32        3050 :         _request = rctx;
      33        3050 :         _originPath = originPath;
      34        3050 :         _subPath = path;
      35        3050 :         _options = data;
      36        3050 :         _subPathVec = UrlView::parsePath<Interface>(_subPath);
      37        3050 :         return OK;
      38             : }
      39             : 
      40        3025 : Status RequestHandler::onPostReadRequest(Request &) { return OK; }
      41          25 : Status RequestHandler::onTranslateName(Request &) { return DECLINED; }
      42         675 : Status RequestHandler::onQuickHandler(Request &, int v) { return DECLINED; }
      43           0 : void RequestHandler::onInsertFilter(Request &) { }
      44           0 : Status RequestHandler::onHandler(Request &) { return DECLINED; }
      45             : 
      46         650 : void RequestHandler::onFilterInit(InputFilter *f) { }
      47         825 : void RequestHandler::onFilterUpdate(InputFilter *f) { }
      48           0 : void RequestHandler::onFilterComplete(InputFilter *f) { }
      49             : 
      50          25 : const Value &RequestHandler::getOptions() const { return _options; }
      51             : 
      52           0 : void RequestHandler::setAccessRole(db::AccessRoleId role) { _accessRole = role; }
      53        3050 : db::AccessRoleId RequestHandler::getAccessRole() const { return _accessRole; }
      54             : 
      55         150 : Status DataHandler::writeResult(Value &data) {
      56         150 :         auto status = _request.getInfo().status;
      57         150 :         if (status >= 400) {
      58           0 :                 return status;
      59             :         }
      60             : 
      61         150 :         data.setInteger(Time::now().toMicros(), "date");
      62             : #if DEBUG
      63         150 :         auto &debug = _request.getDebugMessages();
      64         150 :         if (!debug.empty()) {
      65           0 :                 data.setArray(debug, "debug");
      66             :         }
      67             : #endif
      68         150 :         auto &error = _request.getErrorMessages();
      69         150 :         if (!error.empty()) {
      70          25 :                 data.setArray(error, "errors");
      71             :         }
      72             : 
      73         150 :         _request.writeData(data, allowJsonP());
      74         150 :         return DONE;
      75             : }
      76             : 
      77         150 : static bool isMethodAllowed(RequestMethod r, DataHandler::AllowMethod a) {
      78         150 :         if ((r == RequestMethod::Get && (a & DataHandler::AllowMethod::Get) != 0)
      79           0 :                         || (r == RequestMethod::Delete && (a & DataHandler::AllowMethod::Delete) != 0)
      80           0 :                         || (r == RequestMethod::Put && (a & DataHandler::AllowMethod::Put) != 0)
      81         300 :                         || (r == RequestMethod::Post && (a & DataHandler::AllowMethod::Post) != 0)) {
      82         150 :                 return true;
      83             :         }
      84             : 
      85           0 :         return false;
      86             : }
      87             : 
      88         150 : Status DataHandler::onTranslateName(Request &rctx) {
      89         150 :         if (!isMethodAllowed(rctx.getInfo().method, _allow)) {
      90           0 :                 return HTTP_METHOD_NOT_ALLOWED;
      91             :         }
      92             : 
      93         300 :         if ((rctx.getInfo().method == RequestMethod::Get && (_allow & AllowMethod::Get) != AllowMethod::None)
      94         300 :                         || (rctx.getInfo().method == RequestMethod::Delete && (_allow & AllowMethod::Delete) != AllowMethod::None)) {
      95         150 :                 bool result = false;
      96         150 :                 Value data;
      97             : 
      98         150 :                 Value input;
      99         150 :                 result = processDataHandler(rctx, data, input);
     100         150 :                 data.setBool(result, "OK");
     101         150 :                 return writeResult(data);
     102         150 :         }
     103             : 
     104           0 :         return DECLINED;
     105             : }
     106             : 
     107          25 : void DataHandler::onInsertFilter(Request &rctx) {
     108          50 :         if ((rctx.getInfo().method == RequestMethod::Post && (_allow & AllowMethod::Post) != AllowMethod::None)
     109          50 :                         || (rctx.getInfo().method == RequestMethod::Put && (_allow & AllowMethod::Put) != AllowMethod::None)) {
     110          25 :                 rctx.setInputConfig(_config);
     111             :         }
     112             : 
     113          25 :         if (rctx.getInfo().method == RequestMethod::Put || rctx.getInfo().method == RequestMethod::Post) {
     114          25 :                 auto ex = InputFilter::insert(rctx);
     115          25 :                 if (ex != InputFilter::Exception::None) {
     116           0 :                         if (ex == InputFilter::Exception::TooLarge) {
     117           0 :                                 rctx.setStatus(HTTP_REQUEST_ENTITY_TOO_LARGE);
     118           0 :                         } else if (ex == InputFilter::Exception::Unrecognized) {
     119           0 :                                 rctx.setStatus(HTTP_UNSUPPORTED_MEDIA_TYPE);
     120             :                         }
     121             :                 }
     122             :         }
     123          25 : }
     124             : 
     125          25 : Status DataHandler::onHandler(Request &) {
     126          25 :         return OK;
     127             : }
     128             : 
     129           0 : void DataHandler::onFilterComplete(InputFilter *filter) {
     130           0 :         bool result = false;
     131           0 :         Value data;
     132           0 :         Request rctx(filter->getRequest());
     133           0 :         _filter = filter;
     134             : 
     135           0 :         Value input(filter->getData());
     136           0 :         for (auto &it : filter->getFiles()) {
     137           0 :                 input.setInteger(it.negativeId(), it.name);
     138             :         }
     139             : 
     140           0 :         result = processDataHandler(rctx, data, input);
     141             : 
     142           0 :         data.setBool(result, "OK");
     143           0 :         writeResult(data);
     144           0 : }
     145             : 
     146           0 : FilesystemHandler::FilesystemHandler(const String &path, size_t cacheTime) : _path(path), _cacheTime(cacheTime) { }
     147           0 : FilesystemHandler::FilesystemHandler(const String &path, const String &ct, size_t cacheTime)
     148           0 : : _path(path), _contentType(ct), _cacheTime(cacheTime) { }
     149             : 
     150           0 : bool FilesystemHandler::isRequestPermitted(Request &) {
     151           0 :         return true;
     152             : }
     153           0 : Status FilesystemHandler::onTranslateName(Request &rctx) {
     154           0 :         auto &info = rctx.getInfo();
     155           0 :         if (info.url.path == "/") {
     156           0 :                 return rctx.sendFile(stappler::filesystem::writablePath<Interface>(_path), std::move(_contentType), _cacheTime);
     157             :         } else {
     158           0 :                 auto npath = stappler::filesystem::writablePath<Interface>(info.url.path, true);
     159           0 :                 if (stappler::filesystem::exists(npath) && _subPath != "/") {
     160           0 :                         return DECLINED;
     161             :                 }
     162           0 :                 return rctx.sendFile(stappler::filesystem::writablePath<Interface>(_path), std::move(_contentType), _cacheTime);
     163           0 :         }
     164             : }
     165             : 
     166           0 : bool RequestHandlerMap::Handler::isPermitted() { return false; }
     167         100 : Status RequestHandlerMap::Handler::onRequest() { return DECLINED; }
     168           0 : Value RequestHandlerMap::Handler::onData() { return Value(); }
     169             : 
     170         100 : RequestHandlerMap::Handler::Handler() { }
     171           0 : RequestHandlerMap::Handler::~Handler() { }
     172             : 
     173         100 : void RequestHandlerMap::Handler::onParams(const HandlerInfo *info, Value &&val) {
     174         100 :         _info = info;
     175         100 :         _params = std::move(val);
     176         100 : }
     177             : 
     178         100 : Status RequestHandlerMap::Handler::onTranslateName(Request &rctx) {
     179         100 :         auto &info = rctx.getInfo();
     180         100 :         if (info.method != _info->getMethod()) {
     181           0 :                 return HTTP_METHOD_NOT_ALLOWED;
     182             :         }
     183             : 
     184         100 :         switch (info.method) {
     185         100 :         case RequestMethod::Post:
     186             :         case RequestMethod::Put:
     187             :         case RequestMethod::Patch:
     188         100 :                 return OK;
     189             :                 break;
     190           0 :         default: {
     191           0 :                 if (!processQueryFields(Value(info.queryData))) {
     192           0 :                         return HTTP_BAD_REQUEST;
     193             :                 }
     194           0 :                 bool hasLocation = false;
     195           0 :                 auto ret = onRequest();
     196           0 :                 if (ret == DECLINED) {
     197           0 :                         if (auto data = onData()) {
     198           0 :                                 auto loc = _request.getResponseHeader("Location");
     199           0 :                                 if (!loc.empty()) {
     200           0 :                                         hasLocation = true;
     201             :                                 } else {
     202           0 :                                         data.setBool(true, "OK");
     203           0 :                                         return writeResult(data);
     204             :                                 }
     205             :                         } else {
     206           0 :                                 auto loc = _request.getResponseHeader("Location");
     207           0 :                                 if (!loc.empty()) {
     208           0 :                                         hasLocation = true;
     209             :                                 } else {
     210           0 :                                         Value retVal({ stappler::pair("OK", Value(false)) });
     211           0 :                                         return writeResult(retVal);
     212           0 :                                 }
     213           0 :                         }
     214             :                 }
     215           0 :                 if (ret <= 0 && hasLocation) {
     216           0 :                         return HTTP_SEE_OTHER;
     217             :                 }
     218           0 :                 return ret;
     219             :                 break;
     220             :         }
     221             :         }
     222             :         return HTTP_BAD_REQUEST;
     223             : }
     224             : 
     225         100 : void RequestHandlerMap::Handler::onInsertFilter(Request &rctx) {
     226         100 :         auto &info = rctx.getInfo();
     227         100 :         switch (info.method) {
     228         100 :         case RequestMethod::Post:
     229             :         case RequestMethod::Put:
     230             :         case RequestMethod::Patch: {
     231         100 :                 auto cfg = _info->getInputConfig();
     232         100 :                 if ((cfg.required & db::InputConfig::Require::Data) == db::InputConfig::Require::None) {
     233          25 :                         cfg.required |= db::InputConfig::Require::Data;
     234             :                 }
     235         100 :                 if ((cfg.required & db::InputConfig::Require::Files) == db::InputConfig::Require::None
     236         100 :                                 && (cfg.required & db::InputConfig::Require::FilesAsData) == db::InputConfig::Require::None) {
     237          50 :                         cfg.required |= db::InputConfig::Require::Files;
     238             :                 }
     239         100 :                 rctx.setInputConfig(cfg);
     240             : 
     241         100 :                 auto ex = InputFilter::insert(rctx);
     242         100 :                 if (ex != InputFilter::Exception::None) {
     243           0 :                         if (ex == InputFilter::Exception::TooLarge) {
     244           0 :                                 rctx.setStatus(HTTP_REQUEST_ENTITY_TOO_LARGE);
     245           0 :                         } else if (ex == InputFilter::Exception::Unrecognized) {
     246           0 :                                 rctx.setStatus(HTTP_UNSUPPORTED_MEDIA_TYPE);
     247             :                         }
     248             :                 }
     249         100 :                 break;
     250             :         }
     251           0 :         default:
     252           0 :                 break;
     253             :         }
     254         100 : }
     255             : 
     256         100 : Status RequestHandlerMap::Handler::onHandler(Request &) {
     257         100 :         return OK;
     258             : }
     259             : 
     260         100 : void RequestHandlerMap::Handler::onFilterComplete(InputFilter *filter) {
     261         100 :         auto &info = _request.getInfo();
     262             : 
     263         100 :         _filter = filter;
     264             : 
     265         100 :         if (!processQueryFields(Value(info.queryData))) {
     266           0 :                 _request.setStatus(HTTP_BAD_REQUEST);
     267           0 :                 return;
     268             :         }
     269         100 :         if (!processInputFields(_filter)) {
     270           0 :                 _request.setStatus(HTTP_BAD_REQUEST);
     271           0 :                 return;
     272             :         }
     273             : 
     274         100 :         bool hasLocation = false;
     275         100 :         auto ret = onRequest();
     276         100 :         if (ret == DECLINED) {
     277         100 :                 if (auto data = onData()) {
     278         100 :                         auto loc = _request.getResponseHeader("Location");
     279         100 :                         if (!loc.empty()) {
     280           0 :                                 hasLocation = true;
     281             :                         } else {
     282         100 :                                 data.setBool(true, "OK");
     283         100 :                                 writeResult(data);
     284             :                         }
     285             :                 } else {
     286           0 :                         auto loc = _request.getResponseHeader("Location");
     287           0 :                         if (!loc.empty()) {
     288           0 :                                 hasLocation = true;
     289             :                         } else {
     290           0 :                                 Value retVal({ stappler::pair("OK", Value(false)) });
     291           0 :                                 writeResult(retVal);
     292           0 :                                 return;
     293           0 :                         }
     294         100 :                 }
     295             :         }
     296         100 :         if (ret > 0) {
     297           0 :                 _request.setStatus(ret);
     298         100 :         } else if (hasLocation) {
     299           0 :                 _request.setStatus(HTTP_SEE_OTHER);
     300             :         }
     301             : }
     302             : 
     303         100 : bool RequestHandlerMap::Handler::processQueryFields(Value &&args) {
     304         100 :         _queryFields = std::move(args);
     305         100 :         if (_info->getQueryScheme().getFields().empty()) {
     306          75 :                 return true;
     307             :         }
     308             : 
     309          25 :         _info->getQueryScheme().transform(_queryFields, db::Scheme::TransformAction::ProtectedCreate);
     310             : 
     311          25 :         bool success = true;
     312          75 :         for (auto &it : _info->getQueryScheme().getFields()) {
     313          50 :                 auto &val = _queryFields.getValue(it.first);
     314          50 :                 if (val.isNull() && it.second.hasFlag(db::Flags::Required)) {
     315           0 :                         _request.addError("HandlerMap", "No value for required field",
     316           0 :                                         Value({ std::make_pair("field", Value(it.first)) }));
     317           0 :                         success = false;
     318             :                 }
     319             :         }
     320          25 :         return success;
     321             : }
     322             : 
     323         100 : bool RequestHandlerMap::Handler::processInputFields(InputFilter *filter) {
     324         100 :         _inputFields = std::move(filter->getData());
     325             : 
     326         100 :         if (_info->getInputScheme().getFields().empty()) {
     327          25 :                 return true;
     328             :         }
     329             : 
     330          75 :         _info->getInputScheme().transform(_inputFields, db::Scheme::TransformAction::ProtectedCreate);
     331             : 
     332          75 :         auto &cfg = _request.getInputConfig();
     333          75 :         for (auto &it : filter->getFiles()) {
     334           0 :                 if (auto f = _info->getInputScheme().getField(it.name)) {
     335           0 :                         if ((cfg.required & db::InputConfig::Require::FilesAsData) != db::InputConfig::Require::None && db::InputConfig::isFileAsDataSupportedForType(it.type)) {
     336             :                                 // do nothing
     337             :                         } else {
     338           0 :                                 if (db::File::validateFileField(_request.host().getRoot(), *f, it)) {
     339           0 :                                         _inputFields.setInteger(it.negativeId(), it.name);
     340             :                                 }
     341             :                         }
     342             :                 }
     343             :         }
     344             : 
     345          75 :         bool success = true;
     346         350 :         for (auto &it : _info->getInputScheme().getFields()) {
     347         275 :                 auto &val = _inputFields.getValue(it.first);
     348         275 :                 if (val.isNull() && it.second.hasFlag(db::Flags::Required)) {
     349           0 :                         _request.addError("HandlerMap", "No value for required field",
     350           0 :                                         Value({ std::make_pair("field", Value(it.first)) }));
     351           0 :                         success = false;
     352             :                 }
     353             :         }
     354          75 :         return success;
     355             : }
     356             : 
     357         100 : Status RequestHandlerMap::Handler::writeResult(Value &data) {
     358         100 :         auto status = _request.getInfo().status;
     359         100 :         if (status >= 400) {
     360           0 :                 return status;
     361             :         }
     362             : 
     363         100 :         data.setInteger(Time::now().toMicros(), "date");
     364             : #if DEBUG
     365         100 :         auto &debug = _request.getDebugMessages();
     366         100 :         if (!debug.empty()) {
     367           0 :                 data.setArray(debug, "debug");
     368             :         }
     369             : #endif
     370         100 :         auto &error = _request.getErrorMessages();
     371         100 :         if (!error.empty()) {
     372           0 :                 data.setArray(error, "errors");
     373             :         }
     374             : 
     375         100 :         _request.writeData(data, allowJsonP());
     376         100 :         return DONE;
     377             : }
     378             : 
     379           0 : db::InputFile *RequestHandlerMap::Handler::getInputFile(const StringView &name) {
     380           0 :         if (!_filter) {
     381           0 :                 return nullptr;
     382             :         }
     383             : 
     384           0 :         for (auto &it : _filter->getFiles()) {
     385           0 :                 if (it.name == name) {
     386           0 :                         return &it;
     387             :                 }
     388             :         }
     389             : 
     390           0 :         return nullptr;
     391             : }
     392             : 
     393             : 
     394         100 : RequestHandlerMap::HandlerInfo::HandlerInfo(const StringView &name, RequestMethod m, const StringView &pt,
     395         100 :                 Function<Handler *()> &&cb, Value &&opts)
     396         100 : : name(name.str<Interface>()), method(m), pattern(pt.str<Interface>()), handler(std::move(cb)), options(std::move(opts))
     397         100 : , queryFields(name), inputFields(name) {
     398         100 :         StringView p(pattern);
     399         250 :         while (!p.empty()) {
     400         150 :                 auto tmp = p.readUntil<StringView::Chars<':'>>();
     401         150 :                 if (!tmp.empty()) {
     402         150 :                         fragments.emplace_back(Fragment::Text, tmp);
     403             :                 }
     404         150 :                 if (p.is(':')) {
     405         100 :                         auto tmp = p;
     406         100 :                         ++ p;
     407         100 :                         auto ptrn = p.readUntil<StringView::Chars<'/', '.', ':', '#', '?', ','>>();
     408         100 :                         if (!ptrn.empty()) {
     409         100 :                                 fragments.emplace_back(Fragment::Pattern, StringView(tmp.data(), ptrn.size() + 1));
     410             :                         }
     411             :                 }
     412             :         }
     413         100 : }
     414             : 
     415          50 : RequestHandlerMap::HandlerInfo &RequestHandlerMap::HandlerInfo::addQueryFields(std::initializer_list<db::Field> il) {
     416          50 :         queryFields.define(il);
     417          50 :         return *this;
     418             : }
     419           0 : RequestHandlerMap::HandlerInfo &RequestHandlerMap::HandlerInfo::addQueryFields(Vector<db::Field> &&il) {
     420           0 :         queryFields.define(std::move(il));
     421           0 :         return *this;
     422             : }
     423             : 
     424          50 : RequestHandlerMap::HandlerInfo &RequestHandlerMap::HandlerInfo::addInputFields(std::initializer_list<db::Field> il) {
     425          50 :         inputFields.define(il);
     426          50 :         return *this;
     427             : }
     428           0 : RequestHandlerMap::HandlerInfo &RequestHandlerMap::HandlerInfo::addInputFields(Vector<db::Field> &&il) {
     429           0 :         inputFields.define(std::move(il));
     430           0 :         return *this;
     431             : }
     432             : 
     433          75 : RequestHandlerMap::HandlerInfo &RequestHandlerMap::HandlerInfo::setInputConfig(db::InputConfig cfg) {
     434          75 :         inputFields.setConfig(cfg);
     435          75 :         return *this;
     436             : }
     437             : 
     438         400 : Value RequestHandlerMap::HandlerInfo::match(const StringView &path, size_t &match) const {
     439         400 :         size_t nmatch = 0;
     440        1200 :         Value ret({ stappler::pair("path", Value(path)) });
     441         400 :         auto it = fragments.begin();
     442         400 :         StringView r(path);
     443         975 :         while (!r.empty() && it != fragments.end()) {
     444         700 :                 switch (it->type) {
     445         450 :                 case Fragment::Text:
     446         450 :                         if (r.starts_with(StringView(it->string))) {
     447         325 :                                 r += it->string.size();
     448         325 :                                 nmatch += it->string.size();
     449         325 :                                 ++ it;
     450             :                         } else {
     451         125 :                                 return Value();
     452             :                         }
     453         325 :                         break;
     454         250 :                 case Fragment::Pattern:
     455         250 :                         if (StringView(it->string).is(':')) {
     456         250 :                                 StringView name(it->string.data() + 1, it->string.size() - 1);
     457         250 :                                 if (name.empty()) {
     458           0 :                                         return Value();
     459             :                                 }
     460             : 
     461         250 :                                 ++ it;
     462         250 :                                 if (it != fragments.end()) {
     463         200 :                                         auto tmp = r.readUntilString(it->string);
     464         200 :                                         if (tmp.empty()) {
     465           0 :                                                 return Value();
     466             :                                         }
     467         200 :                                         ret.setString(tmp, name.str<Interface>());
     468             :                                 } else {
     469          50 :                                         ret.setString(r, name.str<Interface>());
     470          50 :                                         r += r.size();
     471             :                                 }
     472             :                         }
     473         250 :                         break;
     474             :                 }
     475             :         }
     476             : 
     477         275 :         if (!r.empty() || it != fragments.end()) {
     478         150 :                 return Value();
     479             :         }
     480             : 
     481         125 :         match = nmatch;
     482         125 :         return ret;
     483         400 : }
     484             : 
     485         100 : RequestHandlerMap::Handler *RequestHandlerMap::HandlerInfo::onHandler(Value &&p) const {
     486         100 :         if (auto h = handler()) {
     487         100 :                 h->onParams(this, std::move(p));
     488         100 :                 return h;
     489             :         }
     490           0 :         return nullptr;
     491             : }
     492             : 
     493         400 : RequestMethod RequestHandlerMap::HandlerInfo::getMethod() const {
     494         400 :         return method;
     495             : }
     496             : 
     497         100 : const db::InputConfig &RequestHandlerMap::HandlerInfo::getInputConfig() const {
     498         100 :         return inputFields.getConfig();
     499             : }
     500             : 
     501         100 : StringView RequestHandlerMap::HandlerInfo::getName() const {
     502         100 :         return name;
     503             : }
     504         100 : StringView RequestHandlerMap::HandlerInfo::getPattern() const {
     505         100 :         return pattern;
     506             : }
     507           0 : const Value &RequestHandlerMap::HandlerInfo::getOptions() const {
     508           0 :         return options;
     509             : }
     510             : 
     511         250 : const db::Scheme &RequestHandlerMap::HandlerInfo::getQueryScheme() const {
     512         250 :         return queryFields;
     513             : }
     514         350 : const db::Scheme &RequestHandlerMap::HandlerInfo::getInputScheme() const {
     515         350 :         return inputFields;
     516             : }
     517             : 
     518          25 : RequestHandlerMap::RequestHandlerMap() { }
     519             : 
     520           0 : RequestHandlerMap::~RequestHandlerMap() { }
     521             : 
     522         100 : RequestHandlerMap::Handler *RequestHandlerMap::onRequest(Request &req, const StringView &ipath) const {
     523         100 :         auto &reqInfo = req.getInfo();
     524         100 :         StringView path(ipath.empty() ? StringView("/") : ipath);
     525         100 :         const HandlerInfo *info = nullptr;
     526         100 :         Value params;
     527         100 :         size_t score = 0;
     528         500 :         for (auto &it : _handlers) {
     529         400 :                 size_t pscore = 0;
     530         400 :                 if (auto val = it.match(path, pscore)) {
     531         125 :                         if (pscore > score || (pscore == score && info && it.getMethod() == reqInfo.method && it.getMethod() != info->getMethod())) {
     532         125 :                                 params = std::move(val);
     533         125 :                                 if (it.getMethod() == reqInfo.method || !info) {
     534         125 :                                         info = &it;
     535         125 :                                         score = pscore;
     536             :                                 }
     537             :                         }
     538         400 :                 }
     539             :         }
     540             : 
     541         100 :         if (info) {
     542         100 :                 return info->onHandler(std::move(params));
     543             :         }
     544             : 
     545           0 :         return nullptr;
     546         100 : }
     547             : 
     548          25 : const Vector<RequestHandlerMap::HandlerInfo> &RequestHandlerMap::getHandlers() const {
     549          25 :         return _handlers;
     550             : }
     551             : 
     552         100 : RequestHandlerMap::HandlerInfo &RequestHandlerMap::addHandler(const StringView &name, RequestMethod m, const StringView &pattern,
     553             :                 Function<Handler *()> &&cb, Value &&opts) {
     554         100 :         _handlers.emplace_back(name, m, pattern, std::move(cb), std::move(opts));
     555         100 :         return _handlers.back();
     556             : }
     557             : 
     558             : 
     559             : class HandlerCallback : public RequestHandlerMap::Handler {
     560             : public: // simplified interface
     561           0 :         HandlerCallback(const Function<bool(Handler &)> &accessControl, const Function<Value(Handler &)> &process)
     562           0 :         : _accessControl(accessControl), _process(process) { }
     563             : 
     564           0 :         virtual ~HandlerCallback() { }
     565             : 
     566           0 :         virtual bool isPermitted() override { return _process && _accessControl && _accessControl(*this); }
     567           0 :         virtual Value onData() override {
     568           0 :                 auto ret = _process(*this);
     569           0 :                 if (ret) {
     570           0 :                         if (_info->getOptions().isString("location")) {
     571           0 :                                 auto locVar = _info->getOptions().getString("location");
     572           0 :                                 auto loc = StringView(_request.getInfo().queryData.getString(locVar));
     573           0 :                                 if (!loc.empty()) {
     574           0 :                                         if (loc.starts_with("/") || loc.starts_with(StringView(_request.getFullHostname()))) {
     575           0 :                                                 _request.redirectTo(loc);
     576             :                                         }
     577             :                                 }
     578           0 :                         }
     579             :                 }
     580           0 :                 return ret;
     581           0 :         }
     582             : 
     583             : public:
     584             :         Function<bool(Handler &)> _accessControl;
     585             :         Function<Value(Handler &)> _process;
     586             : };
     587             : 
     588           0 : RequestHandlerMap::HandlerInfo &RequestHandlerMap::addHandler(const StringView &name, RequestMethod m, const StringView &pattern,
     589             :                 Function<bool(Handler &)> &&accessControl, Function<Value(Handler &)> &&process, Value &&opts) {
     590           0 :         return addHandler(name, m, pattern, [accessControl = std::move(accessControl), process = std::move(process)] () -> Handler * {
     591           0 :                 return new HandlerCallback(accessControl, process);
     592           0 :         }, std::move(opts));
     593             : }
     594             : 
     595             : }

Generated by: LCOV version 1.14