include/boost/burl/message_head_base.hpp
100.0% Lines (8/8)
100.0% List of functions (5/5)
100.0% Branches (6/6)
Functions (5)
Function
Calls
Lines
Branches
Blocks
boost::burl::message_head_base::message_head_base(boost::burl::message_head_base const&)
:129
637x
100.0%
–
100.0%
boost::burl::message_head_base::operator=(boost::burl::message_head_base const&)
:132
99x
100.0%
–
100.0%
boost::burl::message_head_base::buffer() const
:171
59712x
100.0%
–
100.0%
boost::burl::message_head_base::upgrade() const
:285
29x
100.0%
100.0%
100.0%
boost::burl::message_head_base::version() const
:296
22x
100.0%
100.0%
100.0%
| 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 |