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