13 constexpr size_t frame_header_size = 9;
15 constexpr uint32_t stream_id_mask = 0x7FFFFFFF;
17 auto read_uint24_be(std::span<const uint8_t>
data) -> uint32_t
19 return (
static_cast<uint32_t
>(
data[0]) << 16) |
20 (
static_cast<uint32_t
>(
data[1]) << 8) |
21 static_cast<uint32_t
>(
data[2]);
24 auto read_uint32_be(std::span<const uint8_t>
data) -> uint32_t
26 return (
static_cast<uint32_t
>(
data[0]) << 24) |
27 (
static_cast<uint32_t
>(
data[1]) << 16) |
28 (
static_cast<uint32_t
>(
data[2]) << 8) |
29 static_cast<uint32_t
>(
data[3]);
32 auto write_uint24_be(uint32_t value) -> std::array<uint8_t, 3>
35 static_cast<uint8_t
>((value >> 16) & 0xFF),
36 static_cast<uint8_t
>((value >> 8) & 0xFF),
37 static_cast<uint8_t
>(value & 0xFF)
41 auto write_uint32_be(uint32_t value) -> std::array<uint8_t, 4>
44 static_cast<uint8_t
>((value >> 24) & 0xFF),
45 static_cast<uint8_t
>((value >> 16) & 0xFF),
46 static_cast<uint8_t
>((value >> 8) & 0xFF),
47 static_cast<uint8_t
>(value & 0xFF)
54 if (
data.size() < frame_header_size)
56 return error_info(1,
"Insufficient data for frame header",
"http2");
60 header.
length = read_uint24_be(
data.subspan(0, 3));
63 header.
stream_id = read_uint32_be(
data.subspan(5, 4)) & stream_id_mask;
67 return error_info(2,
"Frame size exceeds maximum allowed",
"http2");
75 std::vector<uint8_t> result(frame_header_size);
77 auto length_bytes = write_uint24_be(
length);
78 std::copy(length_bytes.begin(), length_bytes.end(), result.begin());
80 result[3] =
static_cast<uint8_t
>(
type);
83 auto stream_id_bytes = write_uint32_be(
stream_id & stream_id_mask);
84 std::copy(stream_id_bytes.begin(), stream_id_bytes.end(), result.begin() + 5);
90 : header_(hdr), payload_(std::move(payload))
97 if (header_result.is_err())
99 return header_result.error();
102 const auto& header = header_result.value();
104 if (
data.size() < frame_header_size + header.length)
106 return error_info(3,
"Insufficient data for frame payload",
"http2");
109 auto payload_span =
data.subspan(frame_header_size, header.length);
110 std::vector<uint8_t> payload(payload_span.begin(), payload_span.end());
112 std::unique_ptr<frame> result_frame;
119 if (parsed.is_err())
return parsed.error();
120 result_frame = std::move(parsed.value());
126 if (parsed.is_err())
return parsed.error();
127 result_frame = std::move(parsed.value());
133 if (parsed.is_err())
return parsed.error();
134 result_frame = std::move(parsed.value());
140 if (parsed.is_err())
return parsed.error();
141 result_frame = std::move(parsed.value());
147 if (parsed.is_err())
return parsed.error();
148 result_frame = std::move(parsed.value());
154 if (parsed.is_err())
return parsed.error();
155 result_frame = std::move(parsed.value());
161 if (parsed.is_err())
return parsed.error();
162 result_frame = std::move(parsed.value());
166 result_frame = std::make_unique<frame>(header, std::move(payload));
170 return std::move(result_frame);
191 bool end_stream,
bool padded)
192 : data_(std::move(
data))
216 if (hdr.stream_id == 0)
218 return error_info(4,
"DATA frame must have non-zero stream ID",
"http2");
221 std::vector<uint8_t>
data;
222 size_t data_offset = 0;
228 return error_info(5,
"Padded DATA frame must have pad length",
"http2");
231 uint8_t pad_length = payload[0];
234 if (payload.size() < data_offset + pad_length)
236 return error_info(6,
"Invalid padding in DATA frame",
"http2");
239 data.assign(payload.begin() + data_offset,
240 payload.end() - pad_length);
244 data.assign(payload.begin(), payload.end());
247 auto frame = std::make_unique<data_frame>(
273 bool end_stream,
bool end_headers)
274 : header_block_(std::move(header_block))
297 if (hdr.stream_id == 0)
299 return error_info(7,
"HEADERS frame must have non-zero stream ID",
"http2");
302 std::vector<uint8_t> header_block;
309 return error_info(8,
"Padded HEADERS frame must have pad length",
"http2");
312 uint8_t pad_length = payload[0];
315 if (payload.size() < offset + pad_length)
317 return error_info(9,
"Invalid padding in HEADERS frame",
"http2");
320 header_block.assign(payload.begin() + offset,
321 payload.end() - pad_length);
325 header_block.assign(payload.begin(), payload.end());
328 auto frame = std::make_unique<headers_frame>(
330 std::move(header_block),
364 auto id_bytes = write_uint32_be(
static_cast<uint32_t
>(setting.identifier));
367 auto value_bytes = write_uint32_be(setting.value);
378 if (hdr.stream_id != 0)
380 return error_info(10,
"SETTINGS frame must have zero stream ID",
"http2");
385 if (!payload.empty())
387 return error_info(11,
"SETTINGS ACK must have empty payload",
"http2");
390 return std::make_unique<settings_frame>(std::vector<setting_parameter>{},
true);
393 if (payload.size() % 6 != 0)
395 return error_info(12,
"Invalid SETTINGS frame payload size",
"http2");
398 std::vector<setting_parameter>
settings;
399 for (
size_t i = 0; i < payload.size(); i += 6)
401 uint16_t identifier = (
static_cast<uint16_t
>(payload[i]) << 8) |
402 static_cast<uint16_t
>(payload[i + 1]);
403 uint32_t value = read_uint32_be(payload.subspan(i + 2, 4));
405 settings.push_back({identifier, value});
408 return std::make_unique<settings_frame>(std::move(
settings),
false);
430 payload_.assign(error_bytes.begin(), error_bytes.end());
436 if (hdr.stream_id == 0)
438 return error_info(13,
"RST_STREAM frame must have non-zero stream ID",
"http2");
441 if (payload.size() != 4)
443 return error_info(14,
"RST_STREAM frame must have 4-byte payload",
"http2");
446 uint32_t
error_code = read_uint32_be(payload);
447 return std::make_unique<rst_stream_frame>(hdr.stream_id,
error_code);
456 : opaque_data_(opaque_data)
469 if (hdr.stream_id != 0)
471 return error_info(15,
"PING frame must have zero stream ID",
"http2");
474 if (payload.size() != 8)
476 return error_info(16,
"PING frame must have 8-byte payload",
"http2");
479 std::array<uint8_t, 8> opaque_data;
480 std::copy_n(payload.begin(), 8, opaque_data.begin());
482 return std::make_unique<ping_frame>(opaque_data, hdr.flags &
frame_flags::ack);
496 std::vector<uint8_t> additional_data)
497 : last_stream_id_(last_stream_id),
499 additional_data_(std::move(additional_data))
505 auto last_stream_bytes = write_uint32_be(
last_stream_id_ & stream_id_mask);
506 payload_.insert(
payload_.end(), last_stream_bytes.begin(), last_stream_bytes.end());
519 if (hdr.stream_id != 0)
521 return error_info(17,
"GOAWAY frame must have zero stream ID",
"http2");
524 if (payload.size() < 8)
526 return error_info(18,
"GOAWAY frame must have at least 8-byte payload",
"http2");
529 uint32_t last_stream_id = read_uint32_be(payload.subspan(0, 4)) & stream_id_mask;
530 uint32_t
error_code = read_uint32_be(payload.subspan(4, 4));
532 std::vector<uint8_t> additional_data;
533 if (payload.size() > 8)
535 additional_data.assign(payload.begin() + 8, payload.end());
538 return std::make_unique<goaway_frame>(last_stream_id,
error_code,
539 std::move(additional_data));
558 uint32_t window_size_increment)
559 : window_size_increment_(window_size_increment)
567 payload_.assign(increment_bytes.begin(), increment_bytes.end());
573 if (payload.size() != 4)
575 return error_info(19,
"WINDOW_UPDATE frame must have 4-byte payload",
"http2");
578 uint32_t window_size_increment = read_uint32_be(payload) & stream_id_mask;
580 if (window_size_increment == 0)
582 return error_info(20,
"WINDOW_UPDATE increment must be non-zero",
"http2");
585 return std::make_unique<window_update_frame>(hdr.stream_id, window_size_increment);
auto is_end_stream() const -> bool
Check if END_STREAM flag is set.
std::vector< uint8_t > data_
Actual data without padding.
auto is_padded() const -> bool
Check if frame is padded.
static auto parse(const frame_header &hdr, std::span< const uint8_t > payload) -> Result< std::unique_ptr< data_frame > >
Parse DATA frame from raw bytes.
data_frame(uint32_t stream_id, std::vector< uint8_t > data, bool end_stream=false, bool padded=false)
Construct DATA frame.
auto data() const -> std::span< const uint8_t >
Get actual data (without padding)
Base class for HTTP/2 frames.
std::vector< uint8_t > payload_
Frame payload.
auto serialize() const -> std::vector< uint8_t >
Serialize frame to bytes.
frame()=default
Default constructor.
static auto parse(std::span< const uint8_t > data) -> Result< std::unique_ptr< frame > >
Parse frame from raw bytes.
frame_header header_
Frame header.
auto header() const -> const frame_header &
Get frame header.
auto payload() const -> std::span< const uint8_t >
Get frame payload.
auto error_code() const -> uint32_t
Get error code.
std::vector< uint8_t > additional_data_
Debug data.
uint32_t error_code_
Error code.
goaway_frame(uint32_t last_stream_id, uint32_t error_code, std::vector< uint8_t > additional_data={})
Construct GOAWAY frame.
auto additional_data() const -> std::span< const uint8_t >
Get additional debug data.
uint32_t last_stream_id_
Last stream ID.
auto last_stream_id() const -> uint32_t
Get last stream ID.
static auto parse(const frame_header &hdr, std::span< const uint8_t > payload) -> Result< std::unique_ptr< goaway_frame > >
Parse GOAWAY frame from raw bytes.
auto opaque_data() const -> const std::array< uint8_t, 8 > &
Get opaque data.
ping_frame(std::array< uint8_t, 8 > opaque_data={}, bool ack=false)
Construct PING frame.
auto is_ack() const -> bool
Check if this is an ACK frame.
std::array< uint8_t, 8 > opaque_data_
8-byte opaque data
static auto parse(const frame_header &hdr, std::span< const uint8_t > payload) -> Result< std::unique_ptr< ping_frame > >
Parse PING frame from raw bytes.
rst_stream_frame(uint32_t stream_id, uint32_t error_code)
Construct RST_STREAM frame.
static auto parse(const frame_header &hdr, std::span< const uint8_t > payload) -> Result< std::unique_ptr< rst_stream_frame > >
Parse RST_STREAM frame from raw bytes.
uint32_t error_code_
Error code.
auto error_code() const -> uint32_t
Get error code.
static auto parse(const frame_header &hdr, std::span< const uint8_t > payload) -> Result< std::unique_ptr< settings_frame > >
Parse SETTINGS frame from raw bytes.
settings_frame(std::vector< setting_parameter > settings={}, bool ack=false)
Construct SETTINGS frame.
std::vector< setting_parameter > settings_
Settings parameters.
auto settings() const -> const std::vector< setting_parameter > &
Get settings parameters.
auto is_ack() const -> bool
Check if this is an ACK frame.
auto window_size_increment() const -> uint32_t
Get window size increment.
window_update_frame(uint32_t stream_id, uint32_t window_size_increment)
Construct WINDOW_UPDATE frame.
uint32_t window_size_increment_
Window size increment.
static auto parse(const frame_header &hdr, std::span< const uint8_t > payload) -> Result< std::unique_ptr< window_update_frame > >
Parse WINDOW_UPDATE frame from raw bytes.
constexpr uint8_t end_stream
constexpr uint8_t end_headers
error_code
HTTP/2 error codes (RFC 7540 Section 7)
@ max_frame_size
SETTINGS_MAX_FRAME_SIZE.
frame_type
HTTP/2 frame types (RFC 7540 Section 6)
@ settings
SETTINGS frame.
@ rst_stream
RST_STREAM frame.
@ window_update
WINDOW_UPDATE frame.
SETTINGS frame parameter.