Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
frame.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6#include <cstring>
7#include <algorithm>
8
10{
11 namespace
12 {
13 constexpr size_t frame_header_size = 9;
14 constexpr uint32_t max_frame_size = (1 << 24) - 1; // 16,777,215 bytes
15 constexpr uint32_t stream_id_mask = 0x7FFFFFFF; // 31-bit mask
16
17 auto read_uint24_be(std::span<const uint8_t> data) -> uint32_t
18 {
19 return (static_cast<uint32_t>(data[0]) << 16) |
20 (static_cast<uint32_t>(data[1]) << 8) |
21 static_cast<uint32_t>(data[2]);
22 }
23
24 auto read_uint32_be(std::span<const uint8_t> data) -> uint32_t
25 {
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]);
30 }
31
32 auto write_uint24_be(uint32_t value) -> std::array<uint8_t, 3>
33 {
34 return {
35 static_cast<uint8_t>((value >> 16) & 0xFF),
36 static_cast<uint8_t>((value >> 8) & 0xFF),
37 static_cast<uint8_t>(value & 0xFF)
38 };
39 }
40
41 auto write_uint32_be(uint32_t value) -> std::array<uint8_t, 4>
42 {
43 return {
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)
48 };
49 }
50 }
51
52 auto frame_header::parse(std::span<const uint8_t> data) -> Result<frame_header>
53 {
54 if (data.size() < frame_header_size)
55 {
56 return error_info(1, "Insufficient data for frame header", "http2");
57 }
58
59 frame_header header;
60 header.length = read_uint24_be(data.subspan(0, 3));
61 header.type = static_cast<frame_type>(data[3]);
62 header.flags = data[4];
63 header.stream_id = read_uint32_be(data.subspan(5, 4)) & stream_id_mask;
64
65 if (header.length > max_frame_size)
66 {
67 return error_info(2, "Frame size exceeds maximum allowed", "http2");
68 }
69
70 return header;
71 }
72
73 auto frame_header::serialize() const -> std::vector<uint8_t>
74 {
75 std::vector<uint8_t> result(frame_header_size);
76
77 auto length_bytes = write_uint24_be(length);
78 std::copy(length_bytes.begin(), length_bytes.end(), result.begin());
79
80 result[3] = static_cast<uint8_t>(type);
81 result[4] = flags;
82
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);
85
86 return result;
87 }
88
89 frame::frame(const frame_header& hdr, std::vector<uint8_t> payload)
90 : header_(hdr), payload_(std::move(payload))
91 {
92 }
93
94 auto frame::parse(std::span<const uint8_t> data) -> Result<std::unique_ptr<frame>>
95 {
96 auto header_result = frame_header::parse(data);
97 if (header_result.is_err())
98 {
99 return header_result.error();
100 }
101
102 const auto& header = header_result.value();
103
104 if (data.size() < frame_header_size + header.length)
105 {
106 return error_info(3, "Insufficient data for frame payload", "http2");
107 }
108
109 auto payload_span = data.subspan(frame_header_size, header.length);
110 std::vector<uint8_t> payload(payload_span.begin(), payload_span.end());
111
112 std::unique_ptr<frame> result_frame;
113
114 switch (header.type)
115 {
116 case frame_type::data:
117 {
118 auto parsed = data_frame::parse(header, payload);
119 if (parsed.is_err()) return parsed.error();
120 result_frame = std::move(parsed.value());
121 break;
122 }
124 {
125 auto parsed = headers_frame::parse(header, payload);
126 if (parsed.is_err()) return parsed.error();
127 result_frame = std::move(parsed.value());
128 break;
129 }
131 {
132 auto parsed = settings_frame::parse(header, payload);
133 if (parsed.is_err()) return parsed.error();
134 result_frame = std::move(parsed.value());
135 break;
136 }
138 {
139 auto parsed = rst_stream_frame::parse(header, payload);
140 if (parsed.is_err()) return parsed.error();
141 result_frame = std::move(parsed.value());
142 break;
143 }
144 case frame_type::ping:
145 {
146 auto parsed = ping_frame::parse(header, payload);
147 if (parsed.is_err()) return parsed.error();
148 result_frame = std::move(parsed.value());
149 break;
150 }
152 {
153 auto parsed = goaway_frame::parse(header, payload);
154 if (parsed.is_err()) return parsed.error();
155 result_frame = std::move(parsed.value());
156 break;
157 }
159 {
160 auto parsed = window_update_frame::parse(header, payload);
161 if (parsed.is_err()) return parsed.error();
162 result_frame = std::move(parsed.value());
163 break;
164 }
165 default:
166 result_frame = std::make_unique<frame>(header, std::move(payload));
167 break;
168 }
169
170 return std::move(result_frame);
171 }
172
173 auto frame::serialize() const -> std::vector<uint8_t>
174 {
175 auto result = header_.serialize();
176 result.insert(result.end(), payload_.begin(), payload_.end());
177 return result;
178 }
179
180 auto frame::header() const -> const frame_header&
181 {
182 return header_;
183 }
184
185 auto frame::payload() const -> std::span<const uint8_t>
186 {
187 return payload_;
188 }
189
190 data_frame::data_frame(uint32_t stream_id, std::vector<uint8_t> data,
191 bool end_stream, bool padded)
192 : data_(std::move(data))
193 {
194 header_.stream_id = stream_id;
197
198 if (end_stream)
199 {
201 }
202
203 if (padded)
204 {
206 payload_.push_back(0); // Pad length = 0 for now
207 }
208
209 payload_.insert(payload_.end(), data_.begin(), data_.end());
210 header_.length = static_cast<uint32_t>(payload_.size());
211 }
212
213 auto data_frame::parse(const frame_header& hdr, std::span<const uint8_t> payload)
215 {
216 if (hdr.stream_id == 0)
217 {
218 return error_info(4, "DATA frame must have non-zero stream ID", "http2");
219 }
220
221 std::vector<uint8_t> data;
222 size_t data_offset = 0;
223
224 if (hdr.flags & frame_flags::padded)
225 {
226 if (payload.empty())
227 {
228 return error_info(5, "Padded DATA frame must have pad length", "http2");
229 }
230
231 uint8_t pad_length = payload[0];
232 data_offset = 1;
233
234 if (payload.size() < data_offset + pad_length)
235 {
236 return error_info(6, "Invalid padding in DATA frame", "http2");
237 }
238
239 data.assign(payload.begin() + data_offset,
240 payload.end() - pad_length);
241 }
242 else
243 {
244 data.assign(payload.begin(), payload.end());
245 }
246
247 auto frame = std::make_unique<data_frame>(
248 hdr.stream_id,
249 std::move(data),
250 hdr.flags & frame_flags::end_stream,
251 hdr.flags & frame_flags::padded
252 );
253
254 return frame;
255 }
256
257 auto data_frame::is_end_stream() const -> bool
258 {
260 }
261
262 auto data_frame::is_padded() const -> bool
263 {
265 }
266
267 auto data_frame::data() const -> std::span<const uint8_t>
268 {
269 return data_;
270 }
271
272 headers_frame::headers_frame(uint32_t stream_id, std::vector<uint8_t> header_block,
273 bool end_stream, bool end_headers)
274 : header_block_(std::move(header_block))
275 {
276 header_.stream_id = stream_id;
279
280 if (end_stream)
281 {
283 }
284
285 if (end_headers)
286 {
288 }
289
291 header_.length = static_cast<uint32_t>(payload_.size());
292 }
293
294 auto headers_frame::parse(const frame_header& hdr, std::span<const uint8_t> payload)
296 {
297 if (hdr.stream_id == 0)
298 {
299 return error_info(7, "HEADERS frame must have non-zero stream ID", "http2");
300 }
301
302 std::vector<uint8_t> header_block;
303 size_t offset = 0;
304
305 if (hdr.flags & frame_flags::padded)
306 {
307 if (payload.empty())
308 {
309 return error_info(8, "Padded HEADERS frame must have pad length", "http2");
310 }
311
312 uint8_t pad_length = payload[0];
313 offset = 1;
314
315 if (payload.size() < offset + pad_length)
316 {
317 return error_info(9, "Invalid padding in HEADERS frame", "http2");
318 }
319
320 header_block.assign(payload.begin() + offset,
321 payload.end() - pad_length);
322 }
323 else
324 {
325 header_block.assign(payload.begin(), payload.end());
326 }
327
328 auto frame = std::make_unique<headers_frame>(
329 hdr.stream_id,
330 std::move(header_block),
331 hdr.flags & frame_flags::end_stream,
332 hdr.flags & frame_flags::end_headers
333 );
334
335 return frame;
336 }
337
338 auto headers_frame::is_end_stream() const -> bool
339 {
341 }
342
343 auto headers_frame::is_end_headers() const -> bool
344 {
346 }
347
348 auto headers_frame::header_block() const -> std::span<const uint8_t>
349 {
350 return header_block_;
351 }
352
353 settings_frame::settings_frame(std::vector<setting_parameter> settings, bool ack)
354 : settings_(std::move(settings))
355 {
356 header_.stream_id = 0;
359
360 if (!ack)
361 {
362 for (const auto& setting : settings_)
363 {
364 auto id_bytes = write_uint32_be(static_cast<uint32_t>(setting.identifier));
365 payload_.insert(payload_.end(), id_bytes.begin() + 2, id_bytes.end());
366
367 auto value_bytes = write_uint32_be(setting.value);
368 payload_.insert(payload_.end(), value_bytes.begin(), value_bytes.end());
369 }
370 }
371
372 header_.length = static_cast<uint32_t>(payload_.size());
373 }
374
375 auto settings_frame::parse(const frame_header& hdr, std::span<const uint8_t> payload)
377 {
378 if (hdr.stream_id != 0)
379 {
380 return error_info(10, "SETTINGS frame must have zero stream ID", "http2");
381 }
382
383 if (hdr.flags & frame_flags::ack)
384 {
385 if (!payload.empty())
386 {
387 return error_info(11, "SETTINGS ACK must have empty payload", "http2");
388 }
389
390 return std::make_unique<settings_frame>(std::vector<setting_parameter>{}, true);
391 }
392
393 if (payload.size() % 6 != 0)
394 {
395 return error_info(12, "Invalid SETTINGS frame payload size", "http2");
396 }
397
398 std::vector<setting_parameter> settings;
399 for (size_t i = 0; i < payload.size(); i += 6)
400 {
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));
404
405 settings.push_back({identifier, value});
406 }
407
408 return std::make_unique<settings_frame>(std::move(settings), false);
409 }
410
411 auto settings_frame::settings() const -> const std::vector<setting_parameter>&
412 {
413 return settings_;
414 }
415
416 auto settings_frame::is_ack() const -> bool
417 {
419 }
420
421 rst_stream_frame::rst_stream_frame(uint32_t stream_id, uint32_t error_code)
422 : error_code_(error_code)
423 {
424 header_.stream_id = stream_id;
427 header_.length = 4;
428
429 auto error_bytes = write_uint32_be(error_code_);
430 payload_.assign(error_bytes.begin(), error_bytes.end());
431 }
432
433 auto rst_stream_frame::parse(const frame_header& hdr, std::span<const uint8_t> payload)
435 {
436 if (hdr.stream_id == 0)
437 {
438 return error_info(13, "RST_STREAM frame must have non-zero stream ID", "http2");
439 }
440
441 if (payload.size() != 4)
442 {
443 return error_info(14, "RST_STREAM frame must have 4-byte payload", "http2");
444 }
445
446 uint32_t error_code = read_uint32_be(payload);
447 return std::make_unique<rst_stream_frame>(hdr.stream_id, error_code);
448 }
449
450 auto rst_stream_frame::error_code() const -> uint32_t
451 {
452 return error_code_;
453 }
454
455 ping_frame::ping_frame(std::array<uint8_t, 8> opaque_data, bool ack)
456 : opaque_data_(opaque_data)
457 {
458 header_.stream_id = 0;
461 header_.length = 8;
462
463 payload_.assign(opaque_data_.begin(), opaque_data_.end());
464 }
465
466 auto ping_frame::parse(const frame_header& hdr, std::span<const uint8_t> payload)
468 {
469 if (hdr.stream_id != 0)
470 {
471 return error_info(15, "PING frame must have zero stream ID", "http2");
472 }
473
474 if (payload.size() != 8)
475 {
476 return error_info(16, "PING frame must have 8-byte payload", "http2");
477 }
478
479 std::array<uint8_t, 8> opaque_data;
480 std::copy_n(payload.begin(), 8, opaque_data.begin());
481
482 return std::make_unique<ping_frame>(opaque_data, hdr.flags & frame_flags::ack);
483 }
484
485 auto ping_frame::opaque_data() const -> const std::array<uint8_t, 8>&
486 {
487 return opaque_data_;
488 }
489
490 auto ping_frame::is_ack() const -> bool
491 {
493 }
494
495 goaway_frame::goaway_frame(uint32_t last_stream_id, uint32_t error_code,
496 std::vector<uint8_t> additional_data)
497 : last_stream_id_(last_stream_id),
498 error_code_(error_code),
499 additional_data_(std::move(additional_data))
500 {
501 header_.stream_id = 0;
504
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());
507
508 auto error_bytes = write_uint32_be(error_code_);
509 payload_.insert(payload_.end(), error_bytes.begin(), error_bytes.end());
510
511 payload_.insert(payload_.end(), additional_data_.begin(), additional_data_.end());
512
513 header_.length = static_cast<uint32_t>(payload_.size());
514 }
515
516 auto goaway_frame::parse(const frame_header& hdr, std::span<const uint8_t> payload)
518 {
519 if (hdr.stream_id != 0)
520 {
521 return error_info(17, "GOAWAY frame must have zero stream ID", "http2");
522 }
523
524 if (payload.size() < 8)
525 {
526 return error_info(18, "GOAWAY frame must have at least 8-byte payload", "http2");
527 }
528
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));
531
532 std::vector<uint8_t> additional_data;
533 if (payload.size() > 8)
534 {
535 additional_data.assign(payload.begin() + 8, payload.end());
536 }
537
538 return std::make_unique<goaway_frame>(last_stream_id, error_code,
539 std::move(additional_data));
540 }
541
542 auto goaway_frame::last_stream_id() const -> uint32_t
543 {
544 return last_stream_id_;
545 }
546
547 auto goaway_frame::error_code() const -> uint32_t
548 {
549 return error_code_;
550 }
551
552 auto goaway_frame::additional_data() const -> std::span<const uint8_t>
553 {
554 return additional_data_;
555 }
556
558 uint32_t window_size_increment)
559 : window_size_increment_(window_size_increment)
560 {
561 header_.stream_id = stream_id;
564 header_.length = 4;
565
566 auto increment_bytes = write_uint32_be(window_size_increment_ & stream_id_mask);
567 payload_.assign(increment_bytes.begin(), increment_bytes.end());
568 }
569
570 auto window_update_frame::parse(const frame_header& hdr, std::span<const uint8_t> payload)
572 {
573 if (payload.size() != 4)
574 {
575 return error_info(19, "WINDOW_UPDATE frame must have 4-byte payload", "http2");
576 }
577
578 uint32_t window_size_increment = read_uint32_be(payload) & stream_id_mask;
579
580 if (window_size_increment == 0)
581 {
582 return error_info(20, "WINDOW_UPDATE increment must be non-zero", "http2");
583 }
584
585 return std::make_unique<window_update_frame>(hdr.stream_id, window_size_increment);
586 }
587
589 {
591 }
592
593} // namespace kcenon::network::protocols::http2
auto is_end_stream() const -> bool
Check if END_STREAM flag is set.
Definition frame.cpp:257
std::vector< uint8_t > data_
Actual data without padding.
Definition frame.h:179
auto is_padded() const -> bool
Check if frame is padded.
Definition frame.cpp:262
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.
Definition frame.cpp:213
data_frame(uint32_t stream_id, std::vector< uint8_t > data, bool end_stream=false, bool padded=false)
Construct DATA frame.
Definition frame.cpp:190
auto data() const -> std::span< const uint8_t >
Get actual data (without padding)
Definition frame.cpp:267
Base class for HTTP/2 frames.
Definition frame.h:82
std::vector< uint8_t > payload_
Frame payload.
Definition frame.h:128
auto serialize() const -> std::vector< uint8_t >
Serialize frame to bytes.
Definition frame.cpp:173
frame()=default
Default constructor.
static auto parse(std::span< const uint8_t > data) -> Result< std::unique_ptr< frame > >
Parse frame from raw bytes.
Definition frame.cpp:94
frame_header header_
Frame header.
Definition frame.h:127
auto header() const -> const frame_header &
Get frame header.
Definition frame.cpp:180
auto payload() const -> std::span< const uint8_t >
Get frame payload.
Definition frame.cpp:185
auto error_code() const -> uint32_t
Get error code.
Definition frame.cpp:547
std::vector< uint8_t > additional_data_
Debug data.
Definition frame.h:428
goaway_frame(uint32_t last_stream_id, uint32_t error_code, std::vector< uint8_t > additional_data={})
Construct GOAWAY frame.
Definition frame.cpp:495
auto additional_data() const -> std::span< const uint8_t >
Get additional debug data.
Definition frame.cpp:552
uint32_t last_stream_id_
Last stream ID.
Definition frame.h:426
auto last_stream_id() const -> uint32_t
Get last stream ID.
Definition frame.cpp:542
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.
Definition frame.cpp:516
auto header_block() const -> std::span< const uint8_t >
Get header block fragment.
Definition frame.cpp:348
auto is_end_headers() const -> bool
Check if END_HEADERS flag is set.
Definition frame.cpp:343
headers_frame(uint32_t stream_id, std::vector< uint8_t > header_block, bool end_stream=false, bool end_headers=true)
Construct HEADERS frame.
Definition frame.cpp:272
auto is_end_stream() const -> bool
Check if END_STREAM flag is set.
Definition frame.cpp:338
static auto parse(const frame_header &hdr, std::span< const uint8_t > payload) -> Result< std::unique_ptr< headers_frame > >
Parse HEADERS frame from raw bytes.
Definition frame.cpp:294
std::vector< uint8_t > header_block_
Header block fragment.
Definition frame.h:230
auto opaque_data() const -> const std::array< uint8_t, 8 > &
Get opaque data.
Definition frame.cpp:485
ping_frame(std::array< uint8_t, 8 > opaque_data={}, bool ack=false)
Construct PING frame.
Definition frame.cpp:455
auto is_ack() const -> bool
Check if this is an ACK frame.
Definition frame.cpp:490
std::array< uint8_t, 8 > opaque_data_
8-byte opaque data
Definition frame.h:376
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.
Definition frame.cpp:466
rst_stream_frame(uint32_t stream_id, uint32_t error_code)
Construct RST_STREAM frame.
Definition frame.cpp:421
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.
Definition frame.cpp:433
auto error_code() const -> uint32_t
Get error code.
Definition frame.cpp:450
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.
Definition frame.cpp:375
settings_frame(std::vector< setting_parameter > settings={}, bool ack=false)
Construct SETTINGS frame.
Definition frame.cpp:353
std::vector< setting_parameter > settings_
Settings parameters.
Definition frame.h:297
auto settings() const -> const std::vector< setting_parameter > &
Get settings parameters.
Definition frame.cpp:411
auto is_ack() const -> bool
Check if this is an ACK frame.
Definition frame.cpp:416
auto window_size_increment() const -> uint32_t
Get window size increment.
Definition frame.cpp:588
window_update_frame(uint32_t stream_id, uint32_t window_size_increment)
Construct WINDOW_UPDATE frame.
Definition frame.cpp:557
uint32_t window_size_increment_
Window size increment.
Definition frame.h:463
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.
Definition frame.cpp:570
error_code
HTTP/2 error codes (RFC 7540 Section 7)
Definition frame.h:471
frame_type
HTTP/2 frame types (RFC 7540 Section 6)
Definition frame.h:22
simple_error error_info
HTTP/2 frame header (9 bytes)
Definition frame.h:54
uint32_t stream_id
Stream identifier (31 bits, MSB reserved)
Definition frame.h:58
static auto parse(std::span< const uint8_t > data) -> Result< frame_header >
Parse frame header from raw bytes.
Definition frame.cpp:52
auto serialize() const -> std::vector< uint8_t >
Serialize frame header to bytes.
Definition frame.cpp:73
uint32_t length
Payload length (24 bits)
Definition frame.h:55