LCOV - code coverage report
Current view: top level - http/common - http_response.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 94.6 % 202 191
Test Date: 2026-11-10 10:38:07 Functions: 100.0 % 19 19

            Line data    Source code
       1              : #include "http_response.hpp"
       2              : #include "../server/mime_types.hpp"
       3              : #include "../data/out_string.hpp"
       4              : 
       5              : #include <algorithm>
       6              : #include <memory>
       7              : #include "../../util/logger.hpp"
       8              : 
       9              : namespace thinger::http{
      10              : 
      11              :     namespace status_strings{
      12              : 
      13              :         const std::string ok =
      14              :                 "HTTP/1.1 200 OK";
      15              :         const std::string created =
      16              :                 "HTTP/1.1 201 Created";
      17              :         const std::string accepted =
      18              :                 "HTTP/1.1 202 Accepted";
      19              :         const std::string no_content =
      20              :                 "HTTP/1.1 204 No Content";
      21              :         const std::string partial_content =
      22              :                 "HTTP/1.1 206 Partial Content";
      23              :         const std::string multiple_choices =
      24              :                 "HTTP/1.1 300 Multiple Choices";
      25              :         const std::string moved_permanently =
      26              :                 "HTTP/1.1 301 Moved Permanently";
      27              :         const std::string moved_temporarily =
      28              :                 "HTTP/1.1 302 Moved Temporarily";
      29              :         const std::string not_modified =
      30              :                 "HTTP/1.1 304 Not Modified";
      31              :         const std::string bad_request =
      32              :                 "HTTP/1.1 400 Bad Request";
      33              :         const std::string unauthorized =
      34              :                 "HTTP/1.1 401 Unauthorized";
      35              :         const std::string forbidden =
      36              :                 "HTTP/1.1 403 Forbidden";
      37              :         const std::string not_found =
      38              :                 "HTTP/1.1 404 Not Found";
      39              :         const std::string not_allowed =
      40              :                 "HTTP/1.1 405 Method Not Allowed";
      41              :         const std::string timed_out =
      42              :                 "HTTP/1.1 408 Request Timeout";
      43              :         const std::string internal_server_error =
      44              :                 "HTTP/1.1 500 Internal Server Error";
      45              :         const std::string not_implemented =
      46              :                 "HTTP/1.1 501 Not Implemented";
      47              :         const std::string bad_gateway =
      48              :                 "HTTP/1.1 502 Bad Gateway";
      49              :         const std::string service_unavailable =
      50              :                 "HTTP/1.1 503 Service Unavailable";
      51              :         const std::string gateway_timeout =
      52              :                 "HTTP/1.1 504 Gateway Timeout";
      53              :         const std::string switching_protocols =
      54              :                 "HTTP/1.1 101 Switching Protocols";
      55              :         const std::string too_many_requests =
      56              :                 "HTTP/1.1 429 Too Many Requests";
      57              :         const std::string temporary_redirect =
      58              :                 "HTTP/1.1 307 Temporary Redirect";
      59              :         const std::string permanent_redirect =
      60              :                 "HTTP/1.1 308 Permanent Redirect";
      61              :         const std::string upgrade_required =
      62              :                 "HTTP/1.1 426 Upgrade Required";
      63              :         const std::string conflict =
      64              :                 "HTTP/1.1 409 Conflict";
      65              :         const std::string payload_too_large =
      66              :                 "HTTP/1.1 413 Payload Too Large";
      67              :         const std::string unsupported_media_type =
      68              :                 "HTTP/1.1 415 Unsupported Media Type";
      69              :         const std::string range_not_satisfiable =
      70              :                 "HTTP/1.1 416 Range Not Satisfiable";
      71              :         const std::string expectation_failed =
      72              :                 "HTTP/1.1 417 Expectation Failed";
      73              :         const std::string misdirected_request =
      74              :                 "HTTP/1.1 421 Misdirected Request";
      75              :         const std::string request_header_fields_too_large =
      76              :                 "HTTP/1.1 431 Request Header Fields Too Large";
      77              :         const std::string unknown =
      78              :                 "HTTP/1.1 000 Unknown Status";
      79              : 
      80        23736 :         const std::string& get_status_string(http_response::status status){
      81        23736 :             switch(status){
      82        16487 :                 case http_response::status::ok:
      83        16487 :                     return ok;
      84           40 :                 case http_response::status::created:
      85           40 :                     return created;
      86            3 :                 case http_response::status::accepted:
      87            3 :                     return accepted;
      88          111 :                 case http_response::status::no_content:
      89          111 :                     return no_content;
      90           41 :                 case http_response::status::partial_content:
      91           41 :                     return partial_content;
      92            3 :                 case http_response::status::multiple_choices:
      93            3 :                     return multiple_choices;
      94           14 :                 case http_response::status::moved_permanently:
      95           14 :                     return moved_permanently;
      96          371 :                 case http_response::status::moved_temporarily:
      97          371 :                     return moved_temporarily;
      98            9 :                 case http_response::status::temporary_redirect:
      99            9 :                     return temporary_redirect;
     100            9 :                 case http_response::status::permanent_redirect:
     101            9 :                     return permanent_redirect;
     102          100 :                 case http_response::status::not_modified:
     103          100 :                     return not_modified;
     104         1122 :                 case http_response::status::bad_request:
     105         1122 :                     return bad_request;
     106          115 :                 case http_response::status::unauthorized:
     107          115 :                     return unauthorized;
     108          128 :                 case http_response::status::forbidden:
     109          128 :                     return forbidden;
     110          298 :                 case http_response::status::not_found:
     111          298 :                     return not_found;
     112           82 :                 case http_response::status::not_allowed:
     113           82 :                     return not_allowed;
     114          129 :                 case http_response::status::timed_out:
     115          129 :                     return timed_out;
     116           77 :                 case http_response::status::internal_server_error:
     117           77 :                     return internal_server_error;
     118            3 :                 case http_response::status::not_implemented:
     119            3 :                     return not_implemented;
     120           81 :                 case http_response::status::bad_gateway:
     121           81 :                     return bad_gateway;
     122            9 :                 case http_response::status::service_unavailable:
     123            9 :                     return service_unavailable;
     124          180 :                 case http_response::status::gateway_timeout:
     125          180 :                     return gateway_timeout;
     126         3703 :                 case http_response::status::switching_protocols:
     127         3703 :                     return switching_protocols;
     128            3 :                 case http_response::status::too_many_requests:
     129            3 :                     return too_many_requests;
     130           39 :                 case http_response::status::upgrade_required:
     131           39 :                     return upgrade_required;
     132            3 :                 case http_response::status::conflict:
     133            3 :                     return conflict;
     134          302 :                 case http_response::status::payload_too_large:
     135          302 :                     return payload_too_large;
     136           45 :                 case http_response::status::unsupported_media_type:
     137           45 :                     return unsupported_media_type;
     138           10 :                 case http_response::status::range_not_satisfiable:
     139           10 :                     return range_not_satisfiable;
     140           39 :                 case http_response::status::expectation_failed:
     141           39 :                     return expectation_failed;
     142           51 :                 case http_response::status::misdirected_request:
     143           51 :                     return misdirected_request;
     144           57 :                 case http_response::status::request_header_fields_too_large:
     145           57 :                     return request_header_fields_too_large;
     146           72 :                 default:
     147           72 :                     return unknown;
     148              :             }
     149              :         }
     150              :     }
     151              : 
     152        23370 :     void http_response::to_buffer(std::vector<boost::asio::const_buffer>& buffer) const{
     153        23370 :         const auto& status_line = status_strings::get_status_string(status_);
     154        23370 :         auto code = get_status_code();
     155        23370 :         if (&status_line == &status_strings::unknown && code >= 100 && code <= 599) {
     156              :             // any other valid status (e.g. relayed from another server): its code, and its
     157              :             // reason phrase if it only has printable characters (it ends the line)
     158           69 :             status_line_ = "HTTP/1.1 " + std::to_string(code) + " ";
     159           69 :             bool printable = std::all_of(reason_phrase_.begin(), reason_phrase_.end(), [](unsigned char c) {
     160           84 :                 return c == '\t' || (c >= 0x20 && c != 0x7f);
     161              :             });
     162           69 :             if (printable) status_line_ += reason_phrase_;
     163           69 :             buffer.emplace_back(boost::asio::buffer(status_line_));
     164           69 :         } else {
     165        23301 :             buffer.emplace_back(boost::asio::buffer(status_line));
     166              :         }
     167        23370 :         buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
     168        99230 :         for(const auto& t: headers_){
     169        75860 :             buffer.emplace_back(boost::asio::buffer(t.first));
     170        75860 :             buffer.emplace_back(boost::asio::buffer(misc_strings::name_value_separator));
     171        75860 :             buffer.emplace_back(boost::asio::buffer(t.second));
     172        75860 :             buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
     173              :         }
     174        23370 :         buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
     175        23370 :         if(!content_.empty()){
     176        16998 :             buffer.emplace_back(boost::asio::buffer(content_));
     177              :         }
     178        23370 :     }
     179              : 
     180              : 
     181        31103 :     void http_response::log(const char* scope, const body_logging& options) const{
     182        31103 :         if(!LOG_INFO_ENABLED()) return;
     183          348 :         LOG_INFO("[{}] {}", scope, status_strings::get_status_string(status_));
     184          348 :         if(LOG_DEBUG_ENABLED()){
     185          336 :             LOG_DEBUG("Headers:");
     186          336 :             log_headers(options);
     187              :         }
     188          348 :         log_body(content_, content_.empty() ? get_content_length() : content_.size(), options);
     189              :     }
     190              : 
     191              :     namespace stock_replies{
     192              : 
     193              :         static const std::string ok;
     194              : 
     195              :         static const std::string created(
     196              :                 "<html>"
     197              :                 "<head><title>Created</title></head>"
     198              :                 "<body><h1>201 Created</h1></body>"
     199              :                 "</html>");
     200              : 
     201              :         static const std::string accepted(
     202              :                 "<html>"
     203              :                 "<head><title>Accepted</title></head>"
     204              :                 "<body><h1>202 Accepted</h1></body>"
     205              :                 "</html>");
     206              : 
     207              :         static const std::string no_content(
     208              :                 "<html>"
     209              :                 "<head><title>No Content</title></head>"
     210              :                 "<body><h1>204 Content</h1></body>"
     211              :                 "</html>");
     212              : 
     213              :         static const std::string multiple_choices(
     214              :                 "<html>"
     215              :                 "<head><title>Multiple Choices</title></head>"
     216              :                 "<body><h1>300 Multiple Choices</h1></body>"
     217              :                 "</html>");
     218              : 
     219              :         static const std::string moved_permanently(
     220              :                 "<html>"
     221              :                 "<head><title>Moved Permanently</title></head>"
     222              :                 "<body><h1>301 Moved Permanently</h1></body>"
     223              :                 "</html>");
     224              : 
     225              :         static const std::string moved_temporarily(
     226              :                 "<html>"
     227              :                 "<head><title>Moved Temporarily</title></head>"
     228              :                 "<body><h1>302 Moved Temporarily</h1></body>"
     229              :                 "</html>");
     230              : 
     231              :         static const std::string temporary_redirect(
     232              :                 "<html>"
     233              :                 "<head><title>Temporary Redirect</title></head>"
     234              :                 "<body><h1>307 Temporary Redirect</h1></body>"
     235              :                 "</html>");
     236              : 
     237              :         static const std::string permanent_redirect(
     238              :                 "<html>"
     239              :                 "<head><title>Permanet Redirect</title></head>"
     240              :                 "<body><h1>308 Permanent Redirect</h1></body>"
     241              :                 "</html>");
     242              : 
     243              :         static const std::string not_modified(
     244              :                 "<html>"
     245              :                 "<head><title>Not Modified</title></head>"
     246              :                 "<body><h1>304 Not Modified</h1></body>"
     247              :                 "</html>");
     248              : 
     249              :         static const std::string bad_request(
     250              :                 "<html>"
     251              :                 "<head><title>Bad Request</title></head>"
     252              :                 "<body><h1>400 Bad Request</h1></body>"
     253              :                 "</html>");
     254              : 
     255              :         static const std::string unauthorized(
     256              :                 "<html>"
     257              :                 "<head><title>Unauthorized</title></head>"
     258              :                 "<body><h1>401 Unauthorized</h1></body>"
     259              :                 "</html>");
     260              : 
     261              :         static const std::string forbidden(
     262              :                 "<html>"
     263              :                 "<head><title>Forbidden</title></head>"
     264              :                 "<body><h1>403 Forbidden</h1></body>"
     265              :                 "</html>");
     266              : 
     267              :         static const std::string not_found(
     268              :                 "<html>"
     269              :                 "<head><title>Not Found</title></head>"
     270              :                 "<body><h1>404 Not Found</h1></body>"
     271              :                 "</html>");
     272              : 
     273              :         static const std::string internal_server_error(
     274              :                 "<html>"
     275              :                 "<head><title>Internal Server Error</title></head>"
     276              :                 "<body><h1>500 Internal Server Error</h1></body>"
     277              :                 "</html>");
     278              : 
     279              :         static const std::string not_implemented(
     280              :                 "<html>"
     281              :                 "<head><title>Not Implemented</title></head>"
     282              :                 "<body><h1>501 Not Implemented</h1></body>"
     283              :                 "</html>");
     284              : 
     285              :         static const std::string bad_gateway(
     286              :                 "<html>"
     287              :                 "<head><title>Bad Gateway</title></head>"
     288              :                 "<body><h1>502 Bad Gateway</h1></body>"
     289              :                 "</html>");
     290              : 
     291              :         static const std::string service_unavailable(
     292              :                 "<html>"
     293              :                 "<head><title>Service Unavailable</title></head>"
     294              :                 "<body><h1>503 Service Unavailable</h1></body>"
     295              :                 "</html>");
     296              : 
     297              :         static const std::string gateway_timeout(
     298              :                 "<html>"
     299              :                 "<head><title>Gateway Timeout</title></head>"
     300              :                 "<body><h1>504 Gateway Timeout</h1></body>"
     301              :                 "</html>");
     302              : 
     303              :         static const std::string too_many_requests(
     304              :                 "<html>"
     305              :                 "<head><title>Too Many Requests</title></head>"
     306              :                 "<body><h1>429 Too Many Requests</h1></body>"
     307              :                 "</html>");
     308              : 
     309              :         static const std::string timed_out(
     310              :                 "<html>"
     311              :                 "<head><title>Request Timeout</title></head>"
     312              :                 "<body><h1>408 Request Timeout</h1></body>"
     313              :                 "</html>");
     314              : 
     315              :         static const std::string payload_too_large(
     316              :                 "<html>"
     317              :                 "<head><title>Payload Too Large</title></head>"
     318              :                 "<body><h1>413 Payload Too Large</h1></body>"
     319              :                 "</html>");
     320              : 
     321              :         static const std::string unsupported_media_type(
     322              :                 "<html>"
     323              :                 "<head><title>Unsupported Media Type</title></head>"
     324              :                 "<body><h1>415 Unsupported Media Type</h1></body>"
     325              :                 "</html>");
     326              : 
     327              :         static const std::string expectation_failed(
     328              :                 "<html>"
     329              :                 "<head><title>Expectation Failed</title></head>"
     330              :                 "<body><h1>417 Expectation Failed</h1></body>"
     331              :                 "</html>");
     332              : 
     333              :         static const std::string misdirected_request(
     334              :                 "<html>"
     335              :                 "<head><title>Misdirected Request</title></head>"
     336              :                 "<body><h1>421 Misdirected Request</h1></body>"
     337              :                 "</html>");
     338              : 
     339              :         static const std::string request_header_fields_too_large(
     340              :                 "<html>"
     341              :                 "<head><title>Request Header Fields Too Large</title></head>"
     342              :                 "<body><h1>431 Request Header Fields Too Large</h1></body>"
     343              :                 "</html>");
     344              : 
     345          480 :         const std::string& to_string(http_response::status status){
     346          480 :             switch(status){
     347            3 :                 case http_response::status::ok:
     348            3 :                     return ok;
     349            3 :                 case http_response::status::created:
     350            3 :                     return created;
     351            3 :                 case http_response::status::accepted:
     352            3 :                     return accepted;
     353            3 :                 case http_response::status::no_content:
     354            3 :                     return no_content;
     355            0 :                 case http_response::status::multiple_choices:
     356            0 :                     return multiple_choices;
     357            3 :                 case http_response::status::moved_permanently:
     358            3 :                     return moved_permanently;
     359            3 :                 case http_response::status::moved_temporarily:
     360            3 :                     return moved_temporarily;
     361            3 :                 case http_response::status::temporary_redirect:
     362            3 :                     return temporary_redirect;
     363            3 :                 case http_response::status::permanent_redirect:
     364            3 :                     return permanent_redirect;
     365            3 :                 case http_response::status::not_modified:
     366            3 :                     return not_modified;
     367          324 :                 case http_response::status::bad_request:
     368          324 :                     return bad_request;
     369            3 :                 case http_response::status::unauthorized:
     370            3 :                     return unauthorized;
     371            3 :                 case http_response::status::forbidden:
     372            3 :                     return forbidden;
     373            3 :                 case http_response::status::not_found:
     374            3 :                     return not_found;
     375           42 :                 case http_response::status::timed_out:
     376           42 :                     return timed_out;
     377            3 :                 case http_response::status::internal_server_error:
     378            3 :                     return internal_server_error;
     379            0 :                 case http_response::status::not_implemented:
     380            0 :                     return not_implemented;
     381            3 :                 case http_response::status::bad_gateway:
     382            3 :                     return bad_gateway;
     383            3 :                 case http_response::status::service_unavailable:
     384            3 :                     return service_unavailable;
     385            0 :                 case http_response::status::gateway_timeout:
     386            0 :                     return gateway_timeout;
     387            3 :                 case http_response::status::too_many_requests:
     388            3 :                     return too_many_requests;
     389            3 :                 case http_response::status::payload_too_large:
     390            3 :                     return payload_too_large;
     391            0 :                 case http_response::status::unsupported_media_type:
     392            0 :                     return unsupported_media_type;
     393            3 :                 case http_response::status::expectation_failed:
     394            3 :                     return expectation_failed;
     395            3 :                 case http_response::status::misdirected_request:
     396            3 :                     return misdirected_request;
     397           57 :                 case http_response::status::request_header_fields_too_large:
     398           57 :                     return request_header_fields_too_large;
     399            0 :                 default:
     400            0 :                     return internal_server_error;
     401              :             }
     402              :         }
     403              : 
     404              :     } // namespace stock_replies
     405              : 
     406          480 :     std::shared_ptr<http_response> http_response::stock_http_reply(http_response::status status){
     407          480 :         auto response = std::make_shared<http_response>();
     408          480 :         response->set_status(status);
     409          480 :         response->set_content(stock_replies::to_string(status), http::mime_types::text_html);
     410          480 :         return response;
     411            0 :     }
     412              : 
     413           18 :     std::string http_response::get_reason_phrase(http_response::status status){
     414              :         // status lines are "HTTP/1.1 <code> <reason>"
     415           18 :         const auto& line = status_strings::get_status_string(status);
     416           18 :         auto position = line.find(' ', line.find(' ') + 1);
     417           18 :         return position == std::string::npos ? std::string{} : line.substr(position + 1);
     418              :     }
     419              : 
     420       333130 :     http_response::http_response(){
     421              :         // room for the usual headers (connection, content length and type, and a few more)
     422       333130 :         headers_.reserve(8);
     423       333130 :     }
     424              : 
     425         6596 :     bool http_response::is_redirect_response() const{
     426        13183 :         return status_ == status::temporary_redirect ||
     427         6587 :                status_ == status::moved_temporarily ||
     428         6322 :                status_ == status::permanent_redirect ||
     429        19487 :                status_ == status::moved_permanently ||
     430        12900 :                get_status_code() == 303; // See Other, followed with a GET
     431              :     }
     432              : 
     433         8906 :     void http_response::set_status(uint16_t status_code){
     434         8906 :         status_ = (status) status_code;
     435         8906 :     }
     436              : 
     437        13201 :     void http_response::set_status(http_response::status status_code){
     438        13201 :         status_ = status_code;
     439        13201 :     }
     440              : 
     441              : 
     442           12 :     bool http_response::is_ok() const{
     443           12 :         return status_>=status::ok && status_<status::multiple_choices;
     444              :     }
     445              : 
     446            6 :     size_t http_response::get_size(){
     447              :         // Return the size of the content/payload
     448            6 :         return content_.size();
     449              :     }
     450              : 
     451        22424 :     void http_response::set_content(std::string content){
     452        22424 :         content_ = std::move(content);
     453        22424 :         set_content_length(content_.size());
     454        22424 :     }
     455              : 
     456        21722 :     void http_response::set_content(std::string content, std::string content_type){
     457        21722 :         set_content(std::move(content));
     458        21722 :         set_content_type(std::move(content_type));
     459        21722 :     }
     460              : 
     461        23289 :     void http_response::set_content_length(size_t content_length){
     462        23289 :         content_length_ = content_length;
     463        23289 :         replace_header(std::string(http::header::content_length), boost::lexical_cast<std::string>(content_length));
     464        23289 :     }
     465              : 
     466        22286 :     void http_response::set_content_type(std::string&& content_type){
     467        22286 :         set_header(http::header::content_type, std::move(content_type));
     468        22286 :     }
     469              : 
     470         1635 :     void http_response::set_content_type(const std::string& content_type) {
     471         1635 :         set_header(http::header::content_type, content_type);
     472         1635 :     }
     473              : 
     474         9616 :     void http_response::set_reason_phrase(const std::string& reason) {
     475         9616 :         reason_phrase_ = reason;
     476         9616 :     }
     477              : 
     478              : }
        

Generated by: LCOV version 2.0-1