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