55.56% Lines (25/45) 60.00% Functions (6/10)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/http 7   // Official repository: https://github.com/cppalliance/http
8   // 8   //
9   9  
10   #include <boost/http/server/route_handler.hpp> 10   #include <boost/http/server/route_handler.hpp>
11   #include <boost/http/server/etag.hpp> 11   #include <boost/http/server/etag.hpp>
12   #include <boost/http/server/fresh.hpp> 12   #include <boost/http/server/fresh.hpp>
13   #include <boost/http/detail/except.hpp> 13   #include <boost/http/detail/except.hpp>
14   #include <boost/url/grammar/ci_string.hpp> 14   #include <boost/url/grammar/ci_string.hpp>
15   #include <boost/capy/buffers/make_buffer.hpp> 15   #include <boost/capy/buffers/make_buffer.hpp>
16   #include <boost/assert.hpp> 16   #include <boost/assert.hpp>
17   #include <cstring> 17   #include <cstring>
18   18  
19 - namespace std { 19 + namespace boost {
  20 + namespace system {
20   template<> 21   template<>
21   struct is_error_code_enum< 22   struct is_error_code_enum<
22   ::boost::http::route_what> 23   ::boost::http::route_what>
23 - : std::true_type {}; 24 + {
24 - } // std 25 + static bool const value = true;
  26 + };
  27 + } // system
  28 + } // boost
25   29  
26   namespace boost { 30   namespace boost {
27   namespace http { 31   namespace http {
28   32  
29   namespace { 33   namespace {
30   34  
31   struct route_what_cat_type 35   struct route_what_cat_type
32 - : std::error_category 36 + : system::error_category
33   { 37   {
34 - constexpr route_what_cat_type() noexcept = default; 38 + constexpr route_what_cat_type()
  39 + : error_category(0x7a8b3c4d5e6f1029)
  40 + {
  41 + }
35   42  
MISUBC 36   ✗ const char* name() const noexcept override 43   ✗ const char* name() const noexcept override
37   { 44   {
MISUBC 38   ✗ return "boost.http.route_what"; 45   ✗ return "boost.http.route_what";
39   } 46   }
40   47  
MISUBC 41   ✗ std::string message(int code) const override 48   ✗ std::string message(int code) const override
42   { 49   {
MISUNC   50 + ✗ return message(code, nullptr, 0);
  51 + }
  52 +
MISUNC   53 + ✗ char const* message(
  54 + int code,
  55 + char*,
  56 + std::size_t) const noexcept override
  57 + {
MISUBC 43   ✗ switch(static_cast<route_what>(code)) 58   ✗ switch(static_cast<route_what>(code))
44   { 59   {
MISUBC 45   ✗ case route_what::done: return "done"; 60   ✗ case route_what::done: return "done";
MISUBC 46   ✗ case route_what::error: return "error"; 61   ✗ case route_what::error: return "error";
MISUBC 47   ✗ case route_what::next: return "next"; 62   ✗ case route_what::next: return "next";
MISUBC 48   ✗ case route_what::next_route: return "next_route"; 63   ✗ case route_what::next_route: return "next_route";
MISUBC 49   ✗ case route_what::close: return "close"; 64   ✗ case route_what::close: return "close";
MISUBC 50   ✗ default: 65   ✗ default:
MISUBC 51   ✗ return "?"; 66   ✗ return "?";
52   } 67   }
53   } 68   }
54   }; 69   };
55   70  
56   route_what_cat_type route_what_cat; 71   route_what_cat_type route_what_cat;
57   72  
58   } // (anon) 73   } // (anon)
59   74  
60 - std::error_code 75 + system::error_code
HITCBC 61   166 make_error_code( 76   166 make_error_code(
62   route_what w) noexcept 77   route_what w) noexcept
63   { 78   {
ECB 64 - 166 return std::error_code{ 79 + return system::error_code{
HITCBC 65   166 static_cast<int>(w), route_what_cat}; 80   166 static_cast<int>(w), route_what_cat};
66   } 81   }
67   82  
68   //---------------------------------------------------------- 83   //----------------------------------------------------------
69   84  
HITCBC 70   13 route_result:: 85   13 route_result::
71   route_result( 86   route_result(
HITCBC 72 - 13 std::error_code ec) 87 + 13 system::error_code ec)
HITCBC 73   13 : ec_(ec) 88   13 : ec_(ec)
74   { 89   {
HITCBC 75 - 13 if(! ec) 90 + 13 if(! ec.failed())
MISUBC 76   ✗ detail::throw_invalid_argument(); 91   ✗ detail::throw_invalid_argument();
HITCBC 77   13 } 92   13 }
78   93  
79   void 94   void
HITCBC 80   132 route_result:: 95   132 route_result::
81   set(route_what w) 96   set(route_what w)
82   { 97   {
HITCBC 83   132 ec_ = make_error_code(w); 98   132 ec_ = make_error_code(w);
HITCBC 84   132 } 99   132 }
85   100  
86   auto 101   auto
HITCBC 87   456 route_result:: 102   456 route_result::
88   what() const noexcept -> 103   what() const noexcept ->
89   route_what 104   route_what
90   { 105   {
HITCBC 91 - 456 if(! ec_) 106 + 456 if(! ec_.failed())
HITCBC 92   386 return route_what::done; 107   386 return route_what::done;
HITCBC 93   70 if(&ec_.category() != &route_what_cat) 108   70 if(&ec_.category() != &route_what_cat)
HITCBC 94   40 return route_what::error; 109   40 return route_what::error;
HITCBC 95   30 if( ec_ == route_what::next) 110   30 if( ec_ == route_what::next)
HITCBC 96   26 return route_what::next; 111   26 return route_what::next;
HITCBC 97   4 if( ec_ == route_what::next_route) 112   4 if( ec_ == route_what::next_route)
HITCBC 98   3 return route_what::next_route; 113   3 return route_what::next_route;
99   //if( ec_ == route_what::close) 114   //if( ec_ == route_what::close)
HITCBC 100   1 return route_what::close; 115   1 return route_what::close;
101   } 116   }
102   117  
103   auto 118   auto
HITCBC 104   9 route_result:: 119   9 route_result::
105   error() const noexcept -> 120   error() const noexcept ->
106 - std::error_code 121 + system::error_code
107   { 122   {
HITCBC 108   9 if(&ec_.category() != &route_what_cat) 123   9 if(&ec_.category() != &route_what_cat)
HITCBC 109   9 return ec_; 124   9 return ec_;
MISUBC 110   ✗ return {}; 125   ✗ return {};
111   } 126   }
112   127  
113   //------------------------------------------------ 128   //------------------------------------------------
114   129  
115   bool 130   bool
MISUBC 116   ✗ route_params:: 131   ✗ route_params::
117   is_method( 132   is_method(
118   core::string_view s) const noexcept 133   core::string_view s) const noexcept
119   { 134   {
MISUBC 120   ✗ auto m = http::string_to_method(s); 135   ✗ auto m = http::string_to_method(s);
MISUBC 121   ✗ if(m != http::method::unknown) 136   ✗ if(m != http::method::unknown)
MISUBC 122   ✗ return priv_.verb_ == m; 137   ✗ return priv_.verb_ == m;
MISUBC 123   ✗ return s == priv_.verb_str_; 138   ✗ return s == priv_.verb_str_;
124   } 139   }
125   140  
126   //------------------------------------------------ 141   //------------------------------------------------
127   142  
128   capy::io_task<> 143   capy::io_task<>
HITCBC 129   1 route_params:: 144   1 route_params::
130   send(std::string_view body) 145   send(std::string_view body)
131   { 146   {
132   auto const sc = res.status(); 147   auto const sc = res.status();
133   148  
134   // 204 No Content / 304 Not Modified: strip headers, no body 149   // 204 No Content / 304 Not Modified: strip headers, no body
135   if(sc == status::no_content || 150   if(sc == status::no_content ||
136   sc == status::not_modified) 151   sc == status::not_modified)
137   { 152   {
138   res.erase(field::content_type); 153   res.erase(field::content_type);
139   res.erase(field::content_length); 154   res.erase(field::content_length);
140   res.erase(field::transfer_encoding); 155   res.erase(field::transfer_encoding);
141   co_return co_await res_body.write_eof(); 156   co_return co_await res_body.write_eof();
142   } 157   }
143   158  
144   // 205 Reset Content: Content-Length=0, no body 159   // 205 Reset Content: Content-Length=0, no body
145   if(sc == status::reset_content) 160   if(sc == status::reset_content)
146   { 161   {
147   res.erase(field::transfer_encoding); 162   res.erase(field::transfer_encoding);
148   res.set_payload_size(0); 163   res.set_payload_size(0);
149   co_return co_await res_body.write_eof(); 164   co_return co_await res_body.write_eof();
150   } 165   }
151   166  
152   // Set Content-Type if not already set 167   // Set Content-Type if not already set
153   if(! res.exists(field::content_type)) 168   if(! res.exists(field::content_type))
154   { 169   {
155   if(! body.empty() && body[0] == '<') 170   if(! body.empty() && body[0] == '<')
156   res.set(field::content_type, 171   res.set(field::content_type,
157   "text/html; charset=utf-8"); 172   "text/html; charset=utf-8");
158   else 173   else
159   res.set(field::content_type, 174   res.set(field::content_type,
160   "text/plain; charset=utf-8"); 175   "text/plain; charset=utf-8");
161   } 176   }
162   177  
163   // Generate ETag if not already set 178   // Generate ETag if not already set
164   if(! res.exists(field::etag)) 179   if(! res.exists(field::etag))
165   res.set(field::etag, etag(body)); 180   res.set(field::etag, etag(body));
166   181  
167   // Set Content-Length if not already set 182   // Set Content-Length if not already set
168   if(! res.exists(field::content_length)) 183   if(! res.exists(field::content_length))
169   res.set_payload_size(body.size()); 184   res.set_payload_size(body.size());
170   185  
171   // Freshness check: auto-304 for conditional GET 186   // Freshness check: auto-304 for conditional GET
172   if(is_fresh(req, res)) 187   if(is_fresh(req, res))
173   { 188   {
174   res.set_status(status::not_modified); 189   res.set_status(status::not_modified);
175   res.erase(field::content_type); 190   res.erase(field::content_type);
176   res.erase(field::content_length); 191   res.erase(field::content_length);
177   res.erase(field::transfer_encoding); 192   res.erase(field::transfer_encoding);
178   co_return co_await res_body.write_eof(); 193   co_return co_await res_body.write_eof();
179   } 194   }
180   195  
181   // HEAD: send headers only, skip body 196   // HEAD: send headers only, skip body
182   if(req.method() == method::head) 197   if(req.method() == method::head)
183   { 198   {
184   co_return co_await res_body.write_eof(); 199   co_return co_await res_body.write_eof();
185   } 200   }
186   201  
187   auto [ec, n] = co_await res_body.write_eof( 202   auto [ec, n] = co_await res_body.write_eof(
188   capy::make_buffer(body)); 203   capy::make_buffer(body));
189   co_return {ec}; 204   co_return {ec};
HITCBC 190   2 } 205   2 }
191   206  
192   } // http 207   } // http
193   } // boost 208   } // boost