LCOV - code coverage report
Current view: top level - extra/webserver/unix - SPWebUnixRequest.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 200 267 74.9 %
Date: 2024-05-12 00:16:13 Functions: 24 35 68.6 %

          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 "SPWebUnixRequest.h"
      24             : #include "SPWebInputFilter.h"
      25             : #include "SPWebHostController.h"
      26             : 
      27             : namespace STAPPLER_VERSIONIZED stappler::web {
      28             : 
      29        3100 : UnixRequestController::UnixRequestController(pool_t *pool, RequestInfo &&info, ConnectionWorker::Client *client)
      30        3100 : : RequestController(pool, move(info)) {
      31        3100 :         _client = client;
      32             : 
      33        3100 :         _info.useragentIp = client->addr;
      34        3100 :         _info.useragentPort = client->port;
      35        3100 : }
      36             : 
      37          25 : UnixRequestController::UnixRequestController(pool_t *pool, RequestInfo &&info, UnixWebsocketSim *sock)
      38          25 : : RequestController(pool, move(info)) {
      39          25 :         _websocket = sock;
      40          25 :         _info.useragentIp = StringView("127.0.0.1");
      41          25 :         _info.useragentPort = 80;
      42          25 : }
      43             : 
      44           0 : void UnixRequestController::startResponseTransmission() {
      45             : 
      46           0 : }
      47             : 
      48           0 : size_t UnixRequestController::getBytesSent() const {
      49           0 :         return _client->bytesSent;
      50             : }
      51             : 
      52           0 : void UnixRequestController::putc(int c) {
      53           0 :         uint8_t ch = c;
      54           0 :         _client->write(_client->response, &ch, 1);
      55           0 : }
      56             : 
      57     1221125 : size_t UnixRequestController::write(const uint8_t *buf, size_t size) {
      58     1221125 :         _client->write(_client->response, buf, size);
      59     1221125 :         return size;
      60             : }
      61             : 
      62           0 : void UnixRequestController::flush() { }
      63             : 
      64        1500 : bool UnixRequestController::isSecureConnection() const {
      65        1500 :         return false;
      66             : }
      67             : 
      68           0 : void UnixRequestController::setDocumentRoot(StringView val) {
      69           0 :         _info.documentRoot = val.pdup(_pool);
      70           0 : }
      71             : 
      72        2300 : void UnixRequestController::setContentType(StringView val) {
      73        2300 :         _info.contentType = val.pdup(_pool);
      74        2300 : }
      75             : 
      76           0 : void UnixRequestController::setHandler(StringView val) {
      77           0 :         _info.handler = val.pdup(_pool);
      78           0 : }
      79             : 
      80           0 : void UnixRequestController::setContentEncoding(StringView val) {
      81           0 :         _info.contentEncoding = val.pdup(_pool);
      82           0 : }
      83             : 
      84        3125 : void UnixRequestController::setStatus(Status status, StringView val) {
      85        3125 :         _info.status = status;
      86        3125 :         if (!val.empty()) {
      87           0 :                 _info.statusLine = val.pdup(_pool);
      88             :         } else {
      89        3125 :                 _info.statusLine = getStatusLine(Status(status));
      90             :         }
      91        3125 : }
      92             : 
      93         150 : StringView UnixRequestController::getCookie(StringView name, bool removeFromHeadersTable) {
      94         150 :         auto it = _inputCookies.find(name);
      95         150 :         if (it != _inputCookies.end()) {
      96         150 :                 auto ret = it->second;
      97         150 :                 if (removeFromHeadersTable) {
      98          75 :                         _inputCookies.erase(ret);
      99             :                 }
     100         150 :                 return ret;
     101             :         }
     102           0 :         return StringView();
     103             : }
     104             : 
     105         100 : void UnixRequestController::setFilename(StringView val, bool updateStat, Time mtime) {
     106         100 :         if (!val.starts_with(_info.documentRoot)) {
     107           0 :                 auto path = filepath::merge<Interface>(_info.documentRoot, val);
     108           0 :                 if (filesystem::exists(path)) {
     109           0 :                         val = StringView(path).pdup(_pool);
     110             :                 } else {
     111           0 :                         return;
     112             :                 }
     113           0 :         } else {
     114         100 :                 if (filesystem::exists(val)) {
     115         100 :                         val = val.pdup(_pool);
     116             :                 } else {
     117           0 :                         return;
     118             :                 }
     119             :         }
     120             : 
     121         100 :         _info.filename = val;
     122         100 :         if (updateStat) {
     123         100 :                 filesystem::stat(_info.filename, _info.stat);
     124         100 :                 if (mtime != nullptr) {
     125          25 :                         _info.stat.mtime = mtime;
     126             :                 }
     127             :         }
     128             : }
     129             : 
     130       19725 : StringView UnixRequestController::getRequestHeader(StringView key) const {
     131       19725 :         auto tmp = key.str<memory::StandartInterface>();
     132       19725 :         string::apply_tolower_c(tmp);
     133             : 
     134       19725 :         auto it = _requestHeaders.find(StringView(tmp));
     135       19725 :         if (it != _requestHeaders.end()) {
     136        6175 :                 return it->second;
     137             :         }
     138       13550 :         return StringView();
     139       19725 : }
     140             : 
     141         100 : void UnixRequestController::foreachRequestHeaders(const Callback<void(StringView, StringView)> &cb) const {
     142         650 :         for (auto &it : _requestHeaders) {
     143         550 :                 cb(it.first, it.second);
     144             :         }
     145         100 : }
     146             : 
     147       17125 : void UnixRequestController::setRequestHeader(StringView key, StringView val) {
     148       17125 :         auto tmp = key.str<memory::StandartInterface>();
     149       17125 :         string::apply_tolower_c(tmp);
     150             : 
     151       17125 :         auto it = _requestHeaders.find(StringView(tmp));
     152       17125 :         if (it != _requestHeaders.end()) {
     153           0 :                 it->second = val.pdup(_pool);
     154             :         } else {
     155       17125 :                 it = _requestHeaders.emplace(StringView(tmp).pdup(_pool), val.pdup(_pool)).first;
     156             :         }
     157             : 
     158       17125 :         if (it->first == "host") {
     159        3125 :                 StringView r(it->second);
     160        3125 :                 auto h = r.readUntil<StringView::Chars<':'>>();
     161        3125 :                 _info.url.host = h;
     162        3125 :                 if (r.is(':')) {
     163        3100 :                         ++ r;
     164        3100 :                         _info.url.port = r;
     165             :                 }
     166       14000 :         } else if (it->first == "content-length") {
     167         650 :                 _info.contentLength = StringView(it->second).readInteger(10).get(0);
     168       13350 :         } else if (it->first == "cookie") {
     169        2775 :                 auto d = data::readUrlencoded<Interface>(it->second, maxOf<size_t>());
     170        5550 :                 for (auto &iit : d.asDict()) {
     171        2775 :                         _inputCookies.emplace(StringView(iit.first).pdup(_pool), StringView(iit.second.asString()).pdup(_pool));
     172             :                 }
     173        2775 :         }
     174       17125 : }
     175             : 
     176         825 : StringView UnixRequestController::getResponseHeader(StringView key) const {
     177         825 :         auto tmp = key.str<memory::StandartInterface>();
     178         825 :         string::apply_tolower_c(tmp);
     179             : 
     180         825 :         auto it = _responseHeaders.find(StringView(tmp));
     181         825 :         if (it != _responseHeaders.end()) {
     182           0 :                 return it->second;
     183             :         }
     184         825 :         return StringView();
     185         825 : }
     186             : 
     187           0 : void UnixRequestController::foreachResponseHeaders(const Callback<void(StringView, StringView)> &cb) const {
     188           0 :         for (auto &it : _responseHeaders) {
     189           0 :                 cb(it.first, it.second);
     190             :         }
     191           0 : }
     192             : 
     193       11350 : void UnixRequestController::setResponseHeader(StringView key, StringView val) {
     194       11350 :         auto tmp = key.str<memory::StandartInterface>();
     195       11350 :         string::apply_tolower_c(tmp);
     196             : 
     197       11350 :         auto it = _responseHeaders.find(StringView(tmp));
     198       11350 :         if (it != _responseHeaders.end()) {
     199           0 :                 it->second = val.pdup(_pool);
     200             :         } else {
     201       11350 :                 _responseHeaders.emplace(StringView(tmp).pdup(_pool), val.pdup(_pool));
     202             :         }
     203       11350 : }
     204             : 
     205          25 : void UnixRequestController::clearResponseHeaders() {
     206          25 :         _responseHeaders.clear();
     207          25 : }
     208             : 
     209           0 : StringView UnixRequestController::getErrorHeader(StringView key) const {
     210           0 :         auto tmp = key.str<memory::StandartInterface>();
     211           0 :         string::apply_tolower_c(tmp);
     212             : 
     213           0 :         auto it = _errorHeaders.find(StringView(tmp));
     214           0 :         if (it != _errorHeaders.end()) {
     215           0 :                 return it->second;
     216             :         }
     217           0 :         return StringView();
     218           0 : }
     219             : 
     220           0 : void UnixRequestController::foreachErrorHeaders(const Callback<void(StringView, StringView)> &cb) const {
     221           0 :         for (auto &it : _errorHeaders) {
     222           0 :                 cb(it.first, it.second);
     223             :         }
     224           0 : }
     225             : 
     226        6450 : void UnixRequestController::setErrorHeader(StringView key, StringView val) {
     227        6450 :         auto tmp = key.str<memory::StandartInterface>();
     228        6450 :         string::apply_tolower_c(tmp);
     229             : 
     230        6450 :         auto it = _errorHeaders.find(StringView(tmp));
     231        6450 :         if (it != _errorHeaders.end()) {
     232           0 :                 it->second = val.pdup(_pool);
     233             :         } else {
     234        6450 :                 _errorHeaders.emplace(StringView(tmp).pdup(_pool), val.pdup(_pool));
     235             :         }
     236        6450 : }
     237             : 
     238           0 : void UnixRequestController::clearErrorHeaders() {
     239           0 :         _errorHeaders.clear();
     240           0 : }
     241             : 
     242         650 : Status UnixRequestController::processInput(ConnectionWorker::BufferChain &chain) {
     243         650 :         if (!_filter) {
     244           0 :                 return DECLINED;
     245             :         }
     246             : 
     247         650 :         auto ret = chain.read([&, this] (const ConnectionWorker::Buffer *, const uint8_t *data, size_t len) {
     248         850 :                 auto size = std::min(len, size_t(_info.contentLength));
     249         850 :                 BytesView r(data, size);
     250             : 
     251         850 :                 _info.contentLength -= size;
     252             : 
     253         850 :                 bool success = true;
     254         850 :                 perform([&] {
     255         850 :                         if (!_filter->step(r)) {
     256           0 :                                 success = false;
     257             :                         }
     258         850 :                 }, _filter->getPool(), config::TAG_REQUEST, this);
     259             : 
     260         850 :                 if (!success) {
     261           0 :                         return int(DECLINED);
     262             :                 }
     263             : 
     264         850 :                 if (_info.contentLength > 0) {
     265         200 :                         return int(size);
     266             :                 }
     267             : 
     268         650 :                 perform([&] {
     269         650 :                         _filter->finalize();
     270         650 :                 }, _filter->getPool(), config::TAG_REQUEST, this);
     271         650 :                 return int(DONE);
     272             :         }, true);
     273             : 
     274         650 :         if (ret == DECLINED) {
     275           0 :                 _filter = nullptr;
     276           0 :                 return DECLINED;
     277             :         }
     278             : 
     279         650 :         return _info.contentLength > 0 ? SUSPENDED : ret;
     280             : }
     281             : 
     282        3100 : void UnixRequestController::submitResponse(Status status) {
     283        3100 :         if (status > OK) {
     284         475 :                 setStatus(status, StringView());
     285             :         }
     286             : 
     287        3100 :         if (_info.status < HTTP_OK) {
     288        2100 :                 switch (status) {
     289        2100 :                 case DONE:
     290        2100 :                         setStatus(HTTP_OK, StringView());
     291        2100 :                         break;
     292           0 :                 case OK:
     293           0 :                         setStatus(HTTP_OK, StringView());
     294           0 :                         break;
     295           0 :                 case SUSPENDED:
     296             :                 case DECLINED:
     297           0 :                         setStatus(HTTP_BAD_REQUEST, StringView());
     298           0 :                         break;
     299           0 :                 default:
     300           0 :                         break;
     301             :                 }
     302             :         }
     303             : 
     304        3100 :         if (!_info.filename.empty() && !_info.stat.isDir) {
     305          50 :                 _client->writeFile(_client->response, _info.filename, 0, _info.stat.size, ConnectionWorker::Buffer::Eos);
     306             :         }
     307             : 
     308        3100 :         if (_client->response.empty() && !_info.headerRequest) {
     309             :                 // create default response
     310         450 :                 auto result = getDefaultResult();
     311         450 :                 bool allowCbor = isAcceptable("application/cbor") > 0.0f;
     312             : 
     313         450 :                 auto data = data::write<Interface>(result, allowCbor ? data::EncodeFormat::Cbor : data::EncodeFormat::Json);
     314             : 
     315         450 :                 _info.contentType = (allowCbor ? StringView("application/cbor") : StringView("application/json; charset=utf-8"));
     316             : 
     317         450 :                 _client->response.write(_client->pool, data.data(), data.size(), ConnectionWorker::Buffer::Eos);
     318         450 :         } else {
     319        2650 :                 _client->response.write(_client->pool, nullptr, 0, ConnectionWorker::Buffer::Eos);
     320             :         }
     321             : 
     322        3100 :         Time date = Time::now();
     323        3100 :         auto contentLength = _client->response.size();
     324             : 
     325        3100 :         StringView crlf("\r\n");
     326        3100 :         StringView statusLine = getStatusLine(status);
     327        3100 :         if (statusLine.empty()) {
     328           0 :                 statusLine = getStatusLine(HTTP_INTERNAL_SERVER_ERROR);
     329             :         }
     330             : 
     331        3100 :         sp_time_exp_t xt(date);
     332        3100 :         char dateBuf[30] = { 0 };
     333        3100 :         xt.encodeRfc822(dateBuf);
     334             : 
     335       82175 :         auto outFn = [&, this] (StringView str) {
     336       82175 :                 _client->write(_client->output, str);
     337       85275 :         };
     338             : 
     339        3100 :         auto out = Callback<void(StringView)>(outFn);
     340             : 
     341        3100 :         auto writeCookies = [&] (CookieFlags flags) {
     342        3175 :                 for (auto &it : _cookies) {
     343          75 :                         if ((it.second.flags & flags) ==flags) {
     344          75 :                                 out << "set-cookie: " << it.first << "=" << it.second.data;
     345          75 :                                 if (it.second.maxAge) {
     346          50 :                                         out << ";Max-Age=" << it.second.maxAge.toSeconds();
     347             :                                 }
     348          75 :                                 if ((it.second.flags & CookieFlags::HttpOnly) == CookieFlags::HttpOnly) {
     349          75 :                                         out << ";HttpOnly";
     350             :                                 }
     351          75 :                                 auto sameSite = (it.second.flags & CookieFlags::SameSiteStrict);
     352          75 :                                 switch (sameSite) {
     353           0 :                                 case CookieFlags::SameSiteStrict: out << ";SameSite=Strict"; break;
     354           0 :                                 case CookieFlags::SameSiteLux: out << ";SameSite=Lux"; break;
     355          75 :                                 default: out << ";SameSite=None"; break;
     356             :                                 }
     357          75 :                                 out << ";Path=/;Version=1" << crlf;
     358             :                         }
     359             :                 }
     360        3100 :         };
     361             : 
     362        3100 :         out << StringView("HTTP/1.1 ") << statusLine << crlf;
     363        3100 :         if (_info.status >= HTTP_BAD_REQUEST) {
     364         100 :                 setErrorHeader("Date", dateBuf);
     365         100 :                 setErrorHeader("Connection", "close");
     366         100 :                 setErrorHeader("Server", _host->getRoot()->getServerNameLine());
     367         100 :                 if (contentLength) {
     368         100 :                         setErrorHeader("Content-Type", _info.contentType);
     369         100 :                         setErrorHeader("Content-Length", toString(contentLength));
     370             :                 }
     371         100 :                 if (!_info.contentEncoding.empty()) {
     372           0 :                         setErrorHeader("Content-Encoding", _info.contentEncoding);
     373             :                 }
     374             : 
     375         600 :                 for (auto &it : _errorHeaders) {
     376         500 :                         out << it.first << StringView(": ") << it.second << crlf;
     377             :                 }
     378             : 
     379         100 :                 writeCookies(CookieFlags::SetOnError);
     380             :         } else {
     381        3000 :                 setResponseHeader("Date", dateBuf);
     382        3000 :                 setResponseHeader("Connection", "close");
     383        3000 :                 setResponseHeader("Server", _host->getRoot()->getServerNameLine());
     384        3000 :                 if (contentLength) {
     385        2975 :                         setErrorHeader("Content-Type", _info.contentType);
     386        2975 :                         setErrorHeader("Content-Length", toString(contentLength));
     387             :                 }
     388        3000 :                 if (!_info.contentEncoding.empty()) {
     389           0 :                         setErrorHeader("Content-Encoding", _info.contentEncoding);
     390             :                 }
     391             : 
     392        8950 :                 for (auto &it : _errorHeaders) {
     393        5950 :                         auto iit = _responseHeaders.find(it.first);
     394        5950 :                         if (iit == _responseHeaders.end()) {
     395        5625 :                                 _responseHeaders.emplace(it.first, it.second);
     396             :                         }
     397             :                 }
     398             : 
     399       19775 :                 for (auto &it : _responseHeaders) {
     400       16775 :                         out << it.first << StringView(": ") << it.second << crlf;
     401             :                 }
     402             : 
     403        3000 :                 writeCookies(CookieFlags::SetOnSuccess);
     404             :         }
     405             : 
     406        3100 :         if (!_client->response.empty()) {
     407        3075 :                 out << crlf;
     408        3075 :                 _client->write(_client->output, _client->response);
     409             :         } else {
     410          25 :                 _client->write(_client->output, (const uint8_t *)crlf.data(), crlf.size(), ConnectionWorker::Buffer::Eos);
     411             :         }
     412        3100 : }
     413             : 
     414          25 : WebsocketConnection *UnixRequestController::convertToWebsocket(WebsocketHandler *handler, allocator_t *a, pool_t *p) {
     415          25 :         WebsocketConnection *ret = nullptr;
     416          25 :         if (_websocket) {
     417          25 :                 perform([&] {
     418          25 :                         auto r = new (p) UnixWebsocketConnectionSim(a, p, _host, _websocket);
     419          25 :                         _websocket->attachSocket(r);
     420          25 :                         ret = r;
     421          25 :                 }, p);
     422             :         }
     423          25 :         return ret;
     424             : }
     425             : 
     426             : }

Generated by: LCOV version 1.14