include/boost/burl/message_head_base.hpp

100.0% Lines (8/8) 100.0% List of functions (5/5) 100.0% Branches (6/6)
message_head_base.hpp
f(x) Functions (5)
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2021 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Mohammad Nejati
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/burl
9 //
10
11 #ifndef BOOST_BURL_MESSAGE_HEAD_BASE_HPP
12 #define BOOST_BURL_MESSAGE_HEAD_BASE_HPP
13
14 #include <boost/burl/detail/config.hpp>
15 #include <boost/burl/fields_base.hpp>
16
17 #include <boost/http/error.hpp>
18 #include <boost/http/metadata.hpp>
19 #include <boost/http/method.hpp>
20 #include <boost/http/status.hpp>
21 #include <boost/http/version.hpp>
22
23 #include <iosfwd>
24 #include <optional>
25
26 namespace boost
27 {
28 namespace burl
29 {
30
31 /** Mixin for an HTTP message header.
32
33 This type extends @ref fields_base with a start
34 line and framing-related observers and modifiers.
35 The start line is stored immediately before the
36 field section, and @ref buffer returns the whole
37 header.
38
39 Users cannot construct, copy, or destroy objects
40 of this type: headers are obtained by reference
41 from a @ref head_parser, which builds them over
42 the received bytes in their original location, or
43 built directly for sending through the owning
44 @ref request_head and @ref response_head or their
45 static-storage counterparts @ref static_request_head
46 and @ref static_response_head.
47
48 The framing observers @ref payload,
49 @ref content_length, and @ref keep_alive are
50 constant-time: the facts they depend on are
51 cached and kept current as the header is
52 parsed or modified.
53 */
54 class message_head_base : public fields_base
55 {
56 friend class head_parser;
57
58 void
59 push_start_line_(
60 std::string_view method,
61 std::string_view target,
62 http::version v,
63 std::uint16_t n) noexcept;
64
65 void
66 push_start_line_(
67 http::version v,
68 std::uint16_t status_int,
69 std::string_view reason,
70 std::uint16_t n) noexcept;
71
72 http::error
73 push_field_(
74 std::string_view name,
75 std::string_view value,
76 std::uint16_t n) noexcept;
77
78 http::error
79 validate_framing_() const noexcept;
80
81 static
82 std::uint16_t
83 conn_flags_(std::string_view value) noexcept;
84
85 protected:
86 enum : std::uint16_t
87 {
88 f_req = 1 << 0,
89 f_http_1_1 = 1 << 1,
90 f_content_length = 1 << 2,
91 f_transfer_encoding = 1 << 3,
92 f_chunked = 1 << 4,
93 f_conn_close = 1 << 5,
94 f_conn_keep_alive = 1 << 6,
95 f_conn_upgrade = 1 << 7,
96 f_upgrade = 1 << 8,
97 f_exp_100 = 1 << 9
98 };
99
100 struct req_t
101 {
102 std::uint16_t method_len_ = 3; // "GET"
103 std::uint16_t target_len_ = 1; // "/"
104 http::method method_ = http::method::get;
105 };
106
107 struct res_t
108 {
109 std::uint16_t status_int_ = 200;
110 http::status status_ = http::status::ok;
111 };
112
113 std::uint64_t content_length_v_ = 0;
114 union
115 {
116 req_t req_;
117 res_t res_;
118 };
119 std::uint16_t flags_ = f_http_1_1;
120
121 BOOST_BURL_DECL
122 message_head_base(
123 bool is_request,
124 char* base,
125 std::size_t cap,
126 std::uint32_t size,
127 std::uint16_t prefix) noexcept;
128
129 637x message_head_base(message_head_base const&) = default;
130
131 message_head_base&
132 99x operator=(message_head_base const&) = default;
133
134 ~message_head_base() = default;
135
136 BOOST_BURL_DECL
137 void
138 swap_(message_head_base& other) noexcept;
139
140 BOOST_BURL_DECL
141 void
142 set_version_(http::version v) noexcept;
143
144 BOOST_BURL_DECL
145 void
146 on_special_(http::field id) noexcept override;
147
148 BOOST_BURL_DECL
149 void
150 on_clear_() noexcept override;
151
152 public:
153 /** Return a string view representing the header.
154
155 The returned view references the start
156 line, every field line, and the final
157 empty line, forming a complete message
158 header:
159
160 @code
161 "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
162 @endcode
163
164 The view is invalidated when the header is
165 modified.
166
167 @par Complexity
168 Constant.
169 */
170 std::string_view
171 59712x buffer() const noexcept
172 {
173 59712x return { base_(), std::size_t(prefix_) + size_ };
174 }
175
176 /** Format the header to an output stream.
177
178 The start line is written first, then each
179 field as `name: value`, every line followed
180 by a newline:
181
182 @code
183 "GET / HTTP/1.1\nHost: example.com\n"
184 @endcode
185
186 This form is for diagnostics; the wire
187 form is available from @ref buffer.
188
189 @par Complexity
190 Linear in `h.buffer().size()`.
191
192 @par Exception Safety
193 Basic guarantee.
194
195 @return A reference to the output stream.
196
197 @param os The output stream to write to.
198
199 @param h The header to write.
200 */
201 friend
202 BOOST_BURL_DECL
203 std::ostream&
204 operator<<(
205 std::ostream& os,
206 message_head_base const& h);
207
208 /** Return the type of payload framing.
209
210 The result is derived from the
211 Transfer-Encoding and Content-Length
212 fields, and for responses from the status
213 code, following RFC 9112.
214
215 @ref http::payload::error is returned when
216 those fields cannot be reconciled. Such a
217 header is rejected by @ref head_parser, so
218 this only arises for a header built by hand.
219
220 @par Complexity
221 Constant.
222 */
223 BOOST_BURL_DECL
224 http::payload
225 payload() const noexcept;
226
227 /** Return the payload size stated by Content-Length.
228
229 `std::nullopt` is returned when there is no
230 such field, or its value is not a single
231 decimal number, or it appears more than
232 once. Such a header is rejected by
233 @ref head_parser, so this only arises for a
234 header built by hand.
235
236 @par Complexity
237 Constant.
238 */
239 BOOST_BURL_DECL
240 std::optional<std::uint64_t>
241 content_length() const noexcept;
242
243 /** Return true if the payload uses chunked framing.
244
245 This is a shorthand for
246 `payload() == @ref http::payload::chunked`,
247 true when a final "chunked" transfer coding
248 determines the payload framing.
249
250 @par Complexity
251 Constant.
252 */
253 BOOST_BURL_DECL
254 bool
255 chunked() const noexcept;
256
257 /** Return true if the connection should be kept open.
258
259 The result is derived from the HTTP
260 version, the Connection field, and
261 @ref payload.
262
263 @par Complexity
264 Constant.
265 */
266 BOOST_BURL_DECL
267 bool
268 keep_alive() const noexcept;
269
270 /** Return true if the message proposes a protocol switch.
271
272 The result is true when an Upgrade field
273 is present and the Connection field
274 contains the "upgrade" token, following
275 RFC 9110. The proposed protocols are
276 listed in the Upgrade field.
277
278 Recipients of HTTP/1.0 messages must
279 ignore the proposal.
280
281 @par Complexity
282 Constant.
283 */
284 bool
285 29x upgrade() const noexcept
286 {
287
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 2 times.
29x return (flags_ & f_upgrade && flags_ & f_conn_upgrade);
288 }
289
290 /** Return the HTTP version of the message.
291
292 @par Complexity
293 Constant.
294 */
295 http::version
296 22x version() const noexcept
297 {
298 using enum http::version;
299
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 7 times.
22x return (flags_ & f_http_1_1) ? http_1_1 : http_1_0;
300 }
301
302 //--------------------------------------------
303
304 /** Set the Content-Length field.
305
306 Any chunked Transfer-Encoding is removed
307 first, as if by `set_chunked(false)`.
308
309 @par Exception Safety
310 Basic guarantee.
311
312 @throw std::length_error
313 The storage cannot accommodate the field.
314
315 @param n The payload size.
316 */
317 BOOST_BURL_DECL
318 void
319 set_content_length(std::uint64_t n);
320
321 /** Add or remove the chunked transfer coding.
322
323 When `value` is true, any Content-Length
324 fields are removed and "chunked" is added
325 as the final transfer coding if not
326 already present. When `value` is false, a
327 final "chunked" coding is removed.
328
329 @par Exception Safety
330 Basic guarantee.
331
332 @throw std::length_error
333 The storage cannot accommodate the field.
334
335 @param value Whether the payload is chunked.
336 */
337 BOOST_BURL_DECL
338 void
339 set_chunked(bool value);
340
341 /** Set whether the connection should be kept open.
342
343 The Connection field is rewritten: the
344 "close" and "keep-alive" tokens are
345 removed, other tokens are preserved, and
346 the token required by the HTTP version and
347 `value` is added.
348
349 @par Exception Safety
350 Basic guarantee.
351
352 @throw std::length_error
353 The storage cannot accommodate the field.
354
355 @param value Whether the connection should
356 be kept open.
357 */
358 BOOST_BURL_DECL
359 void
360 set_keep_alive(bool value);
361 };
362
363 } // namespace burl
364 } // namespace boost
365
366 #endif
367