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