src/server/router_types.cpp

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