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
7
9{
10
11namespace
12{
13 constexpr const char* source = "quic::frame";
14
15 // Error helper
16 template<typename T>
17 auto make_error(int code, const std::string& message,
18 const std::string& details = "") -> Result<T>
19 {
20 return error<T>(code, message, source, details);
21 }
22} // namespace
23
24// ============================================================================
25// frame_types.h implementations
26// ============================================================================
27
29{
30 return std::visit([](const auto& fr) -> frame_type {
31 using T = std::decay_t<decltype(fr)>;
32 if constexpr (std::is_same_v<T, padding_frame>)
34 else if constexpr (std::is_same_v<T, ping_frame>)
35 return frame_type::ping;
36 else if constexpr (std::is_same_v<T, ack_frame>)
37 return fr.ecn ? frame_type::ack_ecn : frame_type::ack;
38 else if constexpr (std::is_same_v<T, reset_stream_frame>)
40 else if constexpr (std::is_same_v<T, stop_sending_frame>)
42 else if constexpr (std::is_same_v<T, crypto_frame>)
43 return frame_type::crypto;
44 else if constexpr (std::is_same_v<T, new_token_frame>)
46 else if constexpr (std::is_same_v<T, stream_frame>)
48 else if constexpr (std::is_same_v<T, max_data_frame>)
50 else if constexpr (std::is_same_v<T, max_stream_data_frame>)
52 else if constexpr (std::is_same_v<T, max_streams_frame>)
53 return fr.bidirectional ? frame_type::max_streams_bidi
55 else if constexpr (std::is_same_v<T, data_blocked_frame>)
57 else if constexpr (std::is_same_v<T, stream_data_blocked_frame>)
59 else if constexpr (std::is_same_v<T, streams_blocked_frame>)
60 return fr.bidirectional ? frame_type::streams_blocked_bidi
62 else if constexpr (std::is_same_v<T, new_connection_id_frame>)
64 else if constexpr (std::is_same_v<T, retire_connection_id_frame>)
66 else if constexpr (std::is_same_v<T, path_challenge_frame>)
68 else if constexpr (std::is_same_v<T, path_response_frame>)
70 else if constexpr (std::is_same_v<T, connection_close_frame>)
71 return fr.is_application_error ? frame_type::connection_close_app
73 else if constexpr (std::is_same_v<T, handshake_done_frame>)
75 else
77 }, f);
78}
79
80auto frame_type_to_string(frame_type type) -> std::string
81{
82 switch (type)
83 {
84 case frame_type::padding: return "PADDING";
85 case frame_type::ping: return "PING";
86 case frame_type::ack: return "ACK";
87 case frame_type::ack_ecn: return "ACK_ECN";
88 case frame_type::reset_stream: return "RESET_STREAM";
89 case frame_type::stop_sending: return "STOP_SENDING";
90 case frame_type::crypto: return "CRYPTO";
91 case frame_type::new_token: return "NEW_TOKEN";
92 case frame_type::stream_base: return "STREAM";
93 case frame_type::max_data: return "MAX_DATA";
94 case frame_type::max_stream_data: return "MAX_STREAM_DATA";
95 case frame_type::max_streams_bidi: return "MAX_STREAMS_BIDI";
96 case frame_type::max_streams_uni: return "MAX_STREAMS_UNI";
97 case frame_type::data_blocked: return "DATA_BLOCKED";
98 case frame_type::stream_data_blocked: return "STREAM_DATA_BLOCKED";
99 case frame_type::streams_blocked_bidi: return "STREAMS_BLOCKED_BIDI";
100 case frame_type::streams_blocked_uni: return "STREAMS_BLOCKED_UNI";
101 case frame_type::new_connection_id: return "NEW_CONNECTION_ID";
102 case frame_type::retire_connection_id: return "RETIRE_CONNECTION_ID";
103 case frame_type::path_challenge: return "PATH_CHALLENGE";
104 case frame_type::path_response: return "PATH_RESPONSE";
105 case frame_type::connection_close: return "CONNECTION_CLOSE";
106 case frame_type::connection_close_app: return "CONNECTION_CLOSE_APP";
107 case frame_type::handshake_done: return "HANDSHAKE_DONE";
108 default: return "UNKNOWN";
109 }
110}
111
112// ============================================================================
113// frame_parser implementations
114// ============================================================================
115
116auto frame_parser::peek_type(std::span<const uint8_t> data)
118{
119 return varint::decode(data);
120}
121
122auto frame_parser::parse(std::span<const uint8_t> data)
124{
125 if (data.empty())
126 {
127 return make_error<std::pair<frame, size_t>>(
129 "Empty frame data");
130 }
131
132 // Parse frame type
133 auto type_result = varint::decode(data);
134 if (type_result.is_err())
135 {
136 return make_error<std::pair<frame, size_t>>(
138 "Failed to decode frame type");
139 }
140
141 auto [type_value, type_len] = type_result.value();
142 auto remaining = data.subspan(type_len);
143
144 // Check for STREAM frames (0x08-0x0f)
145 if (is_stream_frame(type_value))
146 {
147 auto result = parse_stream(remaining, get_stream_flags(type_value));
148 if (result.is_err()) return result;
149 auto& [f, consumed] = result.value();
150 return ok(std::make_pair(std::move(f), type_len + consumed));
151 }
152
153 // Dispatch based on frame type
154 switch (static_cast<frame_type>(type_value))
155 {
157 return parse_padding(data);
158
159 case frame_type::ping:
160 {
161 auto result = parse_ping(remaining);
162 if (result.is_err()) return result;
163 auto& [f, consumed] = result.value();
164 return ok(std::make_pair(std::move(f), type_len + consumed));
165 }
166
167 case frame_type::ack:
168 {
169 auto result = parse_ack(remaining, false);
170 if (result.is_err()) return result;
171 auto& [f, consumed] = result.value();
172 return ok(std::make_pair(std::move(f), type_len + consumed));
173 }
174
176 {
177 auto result = parse_ack(remaining, true);
178 if (result.is_err()) return result;
179 auto& [f, consumed] = result.value();
180 return ok(std::make_pair(std::move(f), type_len + consumed));
181 }
182
184 {
185 auto result = parse_reset_stream(remaining);
186 if (result.is_err()) return result;
187 auto& [f, consumed] = result.value();
188 return ok(std::make_pair(std::move(f), type_len + consumed));
189 }
190
192 {
193 auto result = parse_stop_sending(remaining);
194 if (result.is_err()) return result;
195 auto& [f, consumed] = result.value();
196 return ok(std::make_pair(std::move(f), type_len + consumed));
197 }
198
200 {
201 auto result = parse_crypto(remaining);
202 if (result.is_err()) return result;
203 auto& [f, consumed] = result.value();
204 return ok(std::make_pair(std::move(f), type_len + consumed));
205 }
206
208 {
209 auto result = parse_new_token(remaining);
210 if (result.is_err()) return result;
211 auto& [f, consumed] = result.value();
212 return ok(std::make_pair(std::move(f), type_len + consumed));
213 }
214
216 {
217 auto result = parse_max_data(remaining);
218 if (result.is_err()) return result;
219 auto& [f, consumed] = result.value();
220 return ok(std::make_pair(std::move(f), type_len + consumed));
221 }
222
224 {
225 auto result = parse_max_stream_data(remaining);
226 if (result.is_err()) return result;
227 auto& [f, consumed] = result.value();
228 return ok(std::make_pair(std::move(f), type_len + consumed));
229 }
230
232 {
233 auto result = parse_max_streams(remaining, true);
234 if (result.is_err()) return result;
235 auto& [f, consumed] = result.value();
236 return ok(std::make_pair(std::move(f), type_len + consumed));
237 }
238
240 {
241 auto result = parse_max_streams(remaining, false);
242 if (result.is_err()) return result;
243 auto& [f, consumed] = result.value();
244 return ok(std::make_pair(std::move(f), type_len + consumed));
245 }
246
248 {
249 auto result = parse_data_blocked(remaining);
250 if (result.is_err()) return result;
251 auto& [f, consumed] = result.value();
252 return ok(std::make_pair(std::move(f), type_len + consumed));
253 }
254
256 {
257 auto result = parse_stream_data_blocked(remaining);
258 if (result.is_err()) return result;
259 auto& [f, consumed] = result.value();
260 return ok(std::make_pair(std::move(f), type_len + consumed));
261 }
262
264 {
265 auto result = parse_streams_blocked(remaining, true);
266 if (result.is_err()) return result;
267 auto& [f, consumed] = result.value();
268 return ok(std::make_pair(std::move(f), type_len + consumed));
269 }
270
272 {
273 auto result = parse_streams_blocked(remaining, false);
274 if (result.is_err()) return result;
275 auto& [f, consumed] = result.value();
276 return ok(std::make_pair(std::move(f), type_len + consumed));
277 }
278
280 {
281 auto result = parse_new_connection_id(remaining);
282 if (result.is_err()) return result;
283 auto& [f, consumed] = result.value();
284 return ok(std::make_pair(std::move(f), type_len + consumed));
285 }
286
288 {
289 auto result = parse_retire_connection_id(remaining);
290 if (result.is_err()) return result;
291 auto& [f, consumed] = result.value();
292 return ok(std::make_pair(std::move(f), type_len + consumed));
293 }
294
296 {
297 auto result = parse_path_challenge(remaining);
298 if (result.is_err()) return result;
299 auto& [f, consumed] = result.value();
300 return ok(std::make_pair(std::move(f), type_len + consumed));
301 }
302
304 {
305 auto result = parse_path_response(remaining);
306 if (result.is_err()) return result;
307 auto& [f, consumed] = result.value();
308 return ok(std::make_pair(std::move(f), type_len + consumed));
309 }
310
312 {
313 auto result = parse_connection_close(remaining, false);
314 if (result.is_err()) return result;
315 auto& [f, consumed] = result.value();
316 return ok(std::make_pair(std::move(f), type_len + consumed));
317 }
318
320 {
321 auto result = parse_connection_close(remaining, true);
322 if (result.is_err()) return result;
323 auto& [f, consumed] = result.value();
324 return ok(std::make_pair(std::move(f), type_len + consumed));
325 }
326
328 {
329 auto result = parse_handshake_done(remaining);
330 if (result.is_err()) return result;
331 auto& [f, consumed] = result.value();
332 return ok(std::make_pair(std::move(f), type_len + consumed));
333 }
334
335 default:
336 return make_error<std::pair<frame, size_t>>(
338 "Unknown frame type",
339 "type=" + std::to_string(type_value));
340 }
341}
342
343auto frame_parser::parse_all(std::span<const uint8_t> data)
345{
346 std::vector<frame> frames;
347 size_t offset = 0;
348
349 while (offset < data.size())
350 {
351 auto result = parse(data.subspan(offset));
352 if (result.is_err())
353 {
354 return make_error<std::vector<frame>>(
356 "Failed to parse frame at offset " + std::to_string(offset));
357 }
358
359 auto& [f, consumed] = result.value();
360 frames.push_back(std::move(f));
361 offset += consumed;
362 }
363
364 return ok(std::move(frames));
365}
366
367auto frame_parser::parse_padding(std::span<const uint8_t> data)
369{
370 // Count consecutive padding bytes
371 size_t count = 0;
372 while (count < data.size() && data[count] == 0x00)
373 {
374 ++count;
375 }
376
377 if (count == 0)
378 {
379 count = 1; // At least one padding byte was consumed (the type)
380 }
381
383 f.count = count;
384 return ok(std::make_pair(frame{f}, count));
385}
386
387auto frame_parser::parse_ping(std::span<const uint8_t> /*data*/)
389{
390 // PING frame has no payload after type
391 return ok(std::make_pair(frame{ping_frame{}}, size_t{0}));
392}
393
394auto frame_parser::parse_ack(std::span<const uint8_t> data, bool has_ecn)
396{
397 size_t offset = 0;
398 ack_frame f;
399
400 // Largest Acknowledged
401 auto largest = varint::decode(data.subspan(offset));
402 if (largest.is_err()) return make_error<std::pair<frame, size_t>>(
403 error_codes::common_errors::invalid_argument, "Failed to parse largest acknowledged");
404 f.largest_acknowledged = largest.value().first;
405 offset += largest.value().second;
406
407 // ACK Delay
408 auto delay = varint::decode(data.subspan(offset));
409 if (delay.is_err()) return make_error<std::pair<frame, size_t>>(
410 error_codes::common_errors::invalid_argument, "Failed to parse ack delay");
411 f.ack_delay = delay.value().first;
412 offset += delay.value().second;
413
414 // ACK Range Count
415 auto range_count = varint::decode(data.subspan(offset));
416 if (range_count.is_err()) return make_error<std::pair<frame, size_t>>(
417 error_codes::common_errors::invalid_argument, "Failed to parse ack range count");
418 offset += range_count.value().second;
419
420 // First ACK Range
421 auto first_range = varint::decode(data.subspan(offset));
422 if (first_range.is_err()) return make_error<std::pair<frame, size_t>>(
423 error_codes::common_errors::invalid_argument, "Failed to parse first ack range");
424 offset += first_range.value().second;
425
426 // Keep the first range too: loss detection and build_ack both use
427 // ranges[0] for the range ending at largest_acknowledged.
428 f.ranges.push_back({0, first_range.value().first});
429 // Additional ranges
430 for (uint64_t i = 0; i < range_count.value().first; ++i)
431 {
432 ack_range range;
433
434 auto gap = varint::decode(data.subspan(offset));
435 if (gap.is_err()) return make_error<std::pair<frame, size_t>>(
436 error_codes::common_errors::invalid_argument, "Failed to parse ack gap");
437 range.gap = gap.value().first;
438 offset += gap.value().second;
439
440 auto length = varint::decode(data.subspan(offset));
441 if (length.is_err()) return make_error<std::pair<frame, size_t>>(
442 error_codes::common_errors::invalid_argument, "Failed to parse ack range length");
443 range.length = length.value().first;
444 offset += length.value().second;
445
446 f.ranges.push_back(range);
447 }
448
449 // ECN Counts (if present)
450 if (has_ecn)
451 {
452 ecn_counts ecn;
453
454 auto ect0 = varint::decode(data.subspan(offset));
455 if (ect0.is_err()) return make_error<std::pair<frame, size_t>>(
456 error_codes::common_errors::invalid_argument, "Failed to parse ECT(0) count");
457 ecn.ect0 = ect0.value().first;
458 offset += ect0.value().second;
459
460 auto ect1 = varint::decode(data.subspan(offset));
461 if (ect1.is_err()) return make_error<std::pair<frame, size_t>>(
462 error_codes::common_errors::invalid_argument, "Failed to parse ECT(1) count");
463 ecn.ect1 = ect1.value().first;
464 offset += ect1.value().second;
465
466 auto ecn_ce = varint::decode(data.subspan(offset));
467 if (ecn_ce.is_err()) return make_error<std::pair<frame, size_t>>(
468 error_codes::common_errors::invalid_argument, "Failed to parse ECN-CE count");
469 ecn.ecn_ce = ecn_ce.value().first;
470 offset += ecn_ce.value().second;
471
472 f.ecn = ecn;
473 }
474
475 return ok(std::make_pair(frame{f}, offset));
476}
477
478auto frame_parser::parse_reset_stream(std::span<const uint8_t> data)
480{
481 size_t offset = 0;
483
484 auto stream_id = varint::decode(data.subspan(offset));
485 if (stream_id.is_err()) return make_error<std::pair<frame, size_t>>(
486 error_codes::common_errors::invalid_argument, "Failed to parse stream id");
487 f.stream_id = stream_id.value().first;
488 offset += stream_id.value().second;
489
490 auto error_code = varint::decode(data.subspan(offset));
491 if (error_code.is_err()) return make_error<std::pair<frame, size_t>>(
492 error_codes::common_errors::invalid_argument, "Failed to parse error code");
493 f.application_error_code = error_code.value().first;
494 offset += error_code.value().second;
495
496 auto final_size = varint::decode(data.subspan(offset));
497 if (final_size.is_err()) return make_error<std::pair<frame, size_t>>(
498 error_codes::common_errors::invalid_argument, "Failed to parse final size");
499 f.final_size = final_size.value().first;
500 offset += final_size.value().second;
501
502 return ok(std::make_pair(frame{f}, offset));
503}
504
505auto frame_parser::parse_stop_sending(std::span<const uint8_t> data)
507{
508 size_t offset = 0;
510
511 auto stream_id = varint::decode(data.subspan(offset));
512 if (stream_id.is_err()) return make_error<std::pair<frame, size_t>>(
513 error_codes::common_errors::invalid_argument, "Failed to parse stream id");
514 f.stream_id = stream_id.value().first;
515 offset += stream_id.value().second;
516
517 auto error_code = varint::decode(data.subspan(offset));
518 if (error_code.is_err()) return make_error<std::pair<frame, size_t>>(
519 error_codes::common_errors::invalid_argument, "Failed to parse error code");
520 f.application_error_code = error_code.value().first;
521 offset += error_code.value().second;
522
523 return ok(std::make_pair(frame{f}, offset));
524}
525
526auto frame_parser::parse_crypto(std::span<const uint8_t> data)
528{
529 size_t offset = 0;
530 crypto_frame f;
531
532 auto crypto_offset = varint::decode(data.subspan(offset));
533 if (crypto_offset.is_err()) return make_error<std::pair<frame, size_t>>(
534 error_codes::common_errors::invalid_argument, "Failed to parse crypto offset");
535 f.offset = crypto_offset.value().first;
536 offset += crypto_offset.value().second;
537
538 auto length = varint::decode(data.subspan(offset));
539 if (length.is_err()) return make_error<std::pair<frame, size_t>>(
540 error_codes::common_errors::invalid_argument, "Failed to parse crypto length");
541 offset += length.value().second;
542
543 if (data.size() - offset < length.value().first)
544 {
545 return make_error<std::pair<frame, size_t>>(
546 error_codes::common_errors::invalid_argument, "Insufficient crypto data");
547 }
548
549 f.data.assign(data.begin() + offset, data.begin() + offset + length.value().first);
550 offset += length.value().first;
551
552 return ok(std::make_pair(frame{f}, offset));
553}
554
555auto frame_parser::parse_new_token(std::span<const uint8_t> data)
557{
558 size_t offset = 0;
560
561 auto length = varint::decode(data.subspan(offset));
562 if (length.is_err()) return make_error<std::pair<frame, size_t>>(
563 error_codes::common_errors::invalid_argument, "Failed to parse token length");
564 offset += length.value().second;
565
566 if (data.size() - offset < length.value().first)
567 {
568 return make_error<std::pair<frame, size_t>>(
569 error_codes::common_errors::invalid_argument, "Insufficient token data");
570 }
571
572 f.token.assign(data.begin() + offset, data.begin() + offset + length.value().first);
573 offset += length.value().first;
574
575 return ok(std::make_pair(frame{f}, offset));
576}
577
578auto frame_parser::parse_stream(std::span<const uint8_t> data, uint8_t flags)
580{
581 size_t offset = 0;
582 stream_frame f;
583
584 // Stream ID
585 auto stream_id = varint::decode(data.subspan(offset));
586 if (stream_id.is_err()) return make_error<std::pair<frame, size_t>>(
587 error_codes::common_errors::invalid_argument, "Failed to parse stream id");
588 f.stream_id = stream_id.value().first;
589 offset += stream_id.value().second;
590
591 // Offset (if OFF bit set)
592 if (flags & stream_flags::off)
593 {
594 auto stream_offset = varint::decode(data.subspan(offset));
595 if (stream_offset.is_err()) return make_error<std::pair<frame, size_t>>(
596 error_codes::common_errors::invalid_argument, "Failed to parse stream offset");
597 f.offset = stream_offset.value().first;
598 offset += stream_offset.value().second;
599 }
600
601 // Length (if LEN bit set)
602 uint64_t length = 0;
603 if (flags & stream_flags::len)
604 {
605 auto len_result = varint::decode(data.subspan(offset));
606 if (len_result.is_err()) return make_error<std::pair<frame, size_t>>(
607 error_codes::common_errors::invalid_argument, "Failed to parse stream length");
608 length = len_result.value().first;
609 offset += len_result.value().second;
610 }
611 else
612 {
613 // Without LEN, data extends to end of packet
614 length = data.size() - offset;
615 }
616
617 // FIN bit
618 f.fin = (flags & stream_flags::fin) != 0;
619
620 // Data
621 if (data.size() - offset < length)
622 {
623 return make_error<std::pair<frame, size_t>>(
624 error_codes::common_errors::invalid_argument, "Insufficient stream data");
625 }
626
627 f.data.assign(data.begin() + offset, data.begin() + offset + length);
628 offset += length;
629
630 return ok(std::make_pair(frame{f}, offset));
631}
632
633auto frame_parser::parse_max_data(std::span<const uint8_t> data)
635{
637
638 auto max_data = varint::decode(data);
639 if (max_data.is_err()) return make_error<std::pair<frame, size_t>>(
640 error_codes::common_errors::invalid_argument, "Failed to parse max data");
641 f.maximum_data = max_data.value().first;
642
643 return ok(std::make_pair(frame{f}, max_data.value().second));
644}
645
646auto frame_parser::parse_max_stream_data(std::span<const uint8_t> data)
648{
649 size_t offset = 0;
651
652 auto stream_id = varint::decode(data.subspan(offset));
653 if (stream_id.is_err()) return make_error<std::pair<frame, size_t>>(
654 error_codes::common_errors::invalid_argument, "Failed to parse stream id");
655 f.stream_id = stream_id.value().first;
656 offset += stream_id.value().second;
657
658 auto max_data = varint::decode(data.subspan(offset));
659 if (max_data.is_err()) return make_error<std::pair<frame, size_t>>(
660 error_codes::common_errors::invalid_argument, "Failed to parse max stream data");
661 f.maximum_stream_data = max_data.value().first;
662 offset += max_data.value().second;
663
664 return ok(std::make_pair(frame{f}, offset));
665}
666
667auto frame_parser::parse_max_streams(std::span<const uint8_t> data, bool bidi)
669{
671 f.bidirectional = bidi;
672
673 auto max_streams = varint::decode(data);
674 if (max_streams.is_err()) return make_error<std::pair<frame, size_t>>(
675 error_codes::common_errors::invalid_argument, "Failed to parse max streams");
676 f.maximum_streams = max_streams.value().first;
677
678 return ok(std::make_pair(frame{f}, max_streams.value().second));
679}
680
681auto frame_parser::parse_data_blocked(std::span<const uint8_t> data)
683{
685
686 auto max_data = varint::decode(data);
687 if (max_data.is_err()) return make_error<std::pair<frame, size_t>>(
688 error_codes::common_errors::invalid_argument, "Failed to parse max data");
689 f.maximum_data = max_data.value().first;
690
691 return ok(std::make_pair(frame{f}, max_data.value().second));
692}
693
694auto frame_parser::parse_stream_data_blocked(std::span<const uint8_t> data)
696{
697 size_t offset = 0;
699
700 auto stream_id = varint::decode(data.subspan(offset));
701 if (stream_id.is_err()) return make_error<std::pair<frame, size_t>>(
702 error_codes::common_errors::invalid_argument, "Failed to parse stream id");
703 f.stream_id = stream_id.value().first;
704 offset += stream_id.value().second;
705
706 auto max_data = varint::decode(data.subspan(offset));
707 if (max_data.is_err()) return make_error<std::pair<frame, size_t>>(
708 error_codes::common_errors::invalid_argument, "Failed to parse max stream data");
709 f.maximum_stream_data = max_data.value().first;
710 offset += max_data.value().second;
711
712 return ok(std::make_pair(frame{f}, offset));
713}
714
715auto frame_parser::parse_streams_blocked(std::span<const uint8_t> data, bool bidi)
717{
719 f.bidirectional = bidi;
720
721 auto max_streams = varint::decode(data);
722 if (max_streams.is_err()) return make_error<std::pair<frame, size_t>>(
723 error_codes::common_errors::invalid_argument, "Failed to parse max streams");
724 f.maximum_streams = max_streams.value().first;
725
726 return ok(std::make_pair(frame{f}, max_streams.value().second));
727}
728
729auto frame_parser::parse_new_connection_id(std::span<const uint8_t> data)
731{
732 size_t offset = 0;
734
735 // Sequence Number
736 auto seq = varint::decode(data.subspan(offset));
737 if (seq.is_err()) return make_error<std::pair<frame, size_t>>(
738 error_codes::common_errors::invalid_argument, "Failed to parse sequence number");
739 f.sequence_number = seq.value().first;
740 offset += seq.value().second;
741
742 // Retire Prior To
743 auto retire = varint::decode(data.subspan(offset));
744 if (retire.is_err()) return make_error<std::pair<frame, size_t>>(
745 error_codes::common_errors::invalid_argument, "Failed to parse retire prior to");
746 f.retire_prior_to = retire.value().first;
747 offset += retire.value().second;
748
749 // Connection ID Length (1 byte, not varint)
750 if (data.size() <= offset)
751 {
752 return make_error<std::pair<frame, size_t>>(
753 error_codes::common_errors::invalid_argument, "Missing connection id length");
754 }
755 uint8_t cid_len = data[offset++];
756 if (cid_len > 20)
757 {
758 return make_error<std::pair<frame, size_t>>(
759 error_codes::common_errors::invalid_argument, "Connection ID too long");
760 }
761
762 // Connection ID
763 if (data.size() - offset < cid_len)
764 {
765 return make_error<std::pair<frame, size_t>>(
766 error_codes::common_errors::invalid_argument, "Insufficient connection id data");
767 }
768 f.connection_id.assign(data.begin() + offset, data.begin() + offset + cid_len);
769 offset += cid_len;
770
771 // Stateless Reset Token (16 bytes)
772 if (data.size() - offset < 16)
773 {
774 return make_error<std::pair<frame, size_t>>(
775 error_codes::common_errors::invalid_argument, "Insufficient reset token data");
776 }
777 std::copy(data.begin() + offset, data.begin() + offset + 16,
778 f.stateless_reset_token.begin());
779 offset += 16;
780
781 return ok(std::make_pair(frame{f}, offset));
782}
783
784auto frame_parser::parse_retire_connection_id(std::span<const uint8_t> data)
786{
788
789 auto seq = varint::decode(data);
790 if (seq.is_err()) return make_error<std::pair<frame, size_t>>(
791 error_codes::common_errors::invalid_argument, "Failed to parse sequence number");
792 f.sequence_number = seq.value().first;
793
794 return ok(std::make_pair(frame{f}, seq.value().second));
795}
796
797auto frame_parser::parse_path_challenge(std::span<const uint8_t> data)
799{
800 if (data.size() < 8)
801 {
802 return make_error<std::pair<frame, size_t>>(
803 error_codes::common_errors::invalid_argument, "Insufficient path challenge data");
804 }
805
807 std::copy(data.begin(), data.begin() + 8, f.data.begin());
808
809 return ok(std::make_pair(frame{f}, size_t{8}));
810}
811
812auto frame_parser::parse_path_response(std::span<const uint8_t> data)
814{
815 if (data.size() < 8)
816 {
817 return make_error<std::pair<frame, size_t>>(
818 error_codes::common_errors::invalid_argument, "Insufficient path response data");
819 }
820
822 std::copy(data.begin(), data.begin() + 8, f.data.begin());
823
824 return ok(std::make_pair(frame{f}, size_t{8}));
825}
826
827auto frame_parser::parse_connection_close(std::span<const uint8_t> data, bool is_app)
829{
830 size_t offset = 0;
832 f.is_application_error = is_app;
833
834 // Error Code
835 auto error_code = varint::decode(data.subspan(offset));
836 if (error_code.is_err()) return make_error<std::pair<frame, size_t>>(
837 error_codes::common_errors::invalid_argument, "Failed to parse error code");
838 f.error_code = error_code.value().first;
839 offset += error_code.value().second;
840
841 // Frame Type (transport close only)
842 if (!is_app)
843 {
844 auto frame_type_val = varint::decode(data.subspan(offset));
845 if (frame_type_val.is_err()) return make_error<std::pair<frame, size_t>>(
846 error_codes::common_errors::invalid_argument, "Failed to parse frame type");
847 f.frame_type = frame_type_val.value().first;
848 offset += frame_type_val.value().second;
849 }
850
851 // Reason Phrase Length
852 auto reason_len = varint::decode(data.subspan(offset));
853 if (reason_len.is_err()) return make_error<std::pair<frame, size_t>>(
854 error_codes::common_errors::invalid_argument, "Failed to parse reason phrase length");
855 offset += reason_len.value().second;
856
857 // Reason Phrase
858 if (data.size() - offset < reason_len.value().first)
859 {
860 return make_error<std::pair<frame, size_t>>(
861 error_codes::common_errors::invalid_argument, "Insufficient reason phrase data");
862 }
863
864 f.reason_phrase.assign(reinterpret_cast<const char*>(data.data() + offset),
865 reason_len.value().first);
866 offset += reason_len.value().first;
867
868 return ok(std::make_pair(frame{f}, offset));
869}
870
871auto frame_parser::parse_handshake_done(std::span<const uint8_t> /*data*/)
873{
874 return ok(std::make_pair(frame{handshake_done_frame{}}, size_t{0}));
875}
876
877// ============================================================================
878// frame_builder implementations
879// ============================================================================
880
881void frame_builder::append_varint(std::vector<uint8_t>& buffer, uint64_t value)
882{
883 auto encoded = varint::encode(value);
884 buffer.insert(buffer.end(), encoded.begin(), encoded.end());
885}
886
887void frame_builder::append_bytes(std::vector<uint8_t>& buffer,
888 std::span<const uint8_t> data)
889{
890 buffer.insert(buffer.end(), data.begin(), data.end());
891}
892
893auto frame_builder::build(const frame& f) -> std::vector<uint8_t>
894{
895 return std::visit([](const auto& fr) -> std::vector<uint8_t> {
896 using T = std::decay_t<decltype(fr)>;
897 if constexpr (std::is_same_v<T, padding_frame>)
898 return build_padding(fr.count);
899 else if constexpr (std::is_same_v<T, ping_frame>)
900 return build_ping();
901 else if constexpr (std::is_same_v<T, ack_frame>)
902 return build_ack(fr);
903 else if constexpr (std::is_same_v<T, reset_stream_frame>)
904 return build_reset_stream(fr);
905 else if constexpr (std::is_same_v<T, stop_sending_frame>)
906 return build_stop_sending(fr);
907 else if constexpr (std::is_same_v<T, crypto_frame>)
908 return build_crypto(fr);
909 else if constexpr (std::is_same_v<T, new_token_frame>)
910 return build_new_token(fr);
911 else if constexpr (std::is_same_v<T, stream_frame>)
912 return build_stream(fr);
913 else if constexpr (std::is_same_v<T, max_data_frame>)
914 return build_max_data(fr);
915 else if constexpr (std::is_same_v<T, max_stream_data_frame>)
916 return build_max_stream_data(fr);
917 else if constexpr (std::is_same_v<T, max_streams_frame>)
918 return build_max_streams(fr);
919 else if constexpr (std::is_same_v<T, data_blocked_frame>)
920 return build_data_blocked(fr);
921 else if constexpr (std::is_same_v<T, stream_data_blocked_frame>)
922 return build_stream_data_blocked(fr);
923 else if constexpr (std::is_same_v<T, streams_blocked_frame>)
924 return build_streams_blocked(fr);
925 else if constexpr (std::is_same_v<T, new_connection_id_frame>)
926 return build_new_connection_id(fr);
927 else if constexpr (std::is_same_v<T, retire_connection_id_frame>)
928 return build_retire_connection_id(fr);
929 else if constexpr (std::is_same_v<T, path_challenge_frame>)
930 return build_path_challenge(fr);
931 else if constexpr (std::is_same_v<T, path_response_frame>)
932 return build_path_response(fr);
933 else if constexpr (std::is_same_v<T, connection_close_frame>)
934 return build_connection_close(fr);
935 else if constexpr (std::is_same_v<T, handshake_done_frame>)
936 return build_handshake_done();
937 else
938 return {};
939 }, f);
940}
941
942auto frame_builder::build_padding(size_t count) -> std::vector<uint8_t>
943{
944 return std::vector<uint8_t>(count, 0x00);
945}
946
947auto frame_builder::build_ping() -> std::vector<uint8_t>
948{
949 return {0x01};
950}
951
952auto frame_builder::build_ack(const ack_frame& f) -> std::vector<uint8_t>
953{
954 std::vector<uint8_t> buffer;
955
956 // Frame type
957 append_varint(buffer, f.ecn ? static_cast<uint64_t>(frame_type::ack_ecn)
958 : static_cast<uint64_t>(frame_type::ack));
959
960 // Largest Acknowledged
961 append_varint(buffer, f.largest_acknowledged);
962
963 // ACK Delay
964 append_varint(buffer, f.ack_delay);
965
966 // ACK Range Count
967 append_varint(buffer, f.ranges.empty() ? 0 : f.ranges.size() - 1);
968
969 // First ACK Range (largest_ack - smallest_ack in first range)
970 // For simplicity, we'll use 0 if no ranges are specified
971 uint64_t first_range = 0;
972 if (!f.ranges.empty())
973 {
974 first_range = f.ranges[0].length;
975 }
976 append_varint(buffer, first_range);
977
978 // Additional ranges (skip first)
979 for (size_t i = 1; i < f.ranges.size(); ++i)
980 {
981 append_varint(buffer, f.ranges[i].gap);
982 append_varint(buffer, f.ranges[i].length);
983 }
984
985 // ECN Counts
986 if (f.ecn)
987 {
988 append_varint(buffer, f.ecn->ect0);
989 append_varint(buffer, f.ecn->ect1);
990 append_varint(buffer, f.ecn->ecn_ce);
991 }
992
993 return buffer;
994}
995
997 -> std::vector<uint8_t>
998{
999 std::vector<uint8_t> buffer;
1000 append_varint(buffer, static_cast<uint64_t>(frame_type::reset_stream));
1001 append_varint(buffer, f.stream_id);
1002 append_varint(buffer, f.application_error_code);
1003 append_varint(buffer, f.final_size);
1004 return buffer;
1005}
1006
1008 -> std::vector<uint8_t>
1009{
1010 std::vector<uint8_t> buffer;
1011 append_varint(buffer, static_cast<uint64_t>(frame_type::stop_sending));
1012 append_varint(buffer, f.stream_id);
1013 append_varint(buffer, f.application_error_code);
1014 return buffer;
1015}
1016
1017auto frame_builder::build_crypto(const crypto_frame& f) -> std::vector<uint8_t>
1018{
1019 std::vector<uint8_t> buffer;
1020 append_varint(buffer, static_cast<uint64_t>(frame_type::crypto));
1021 append_varint(buffer, f.offset);
1022 append_varint(buffer, f.data.size());
1023 append_bytes(buffer, f.data);
1024 return buffer;
1025}
1026
1028 -> std::vector<uint8_t>
1029{
1030 std::vector<uint8_t> buffer;
1031 append_varint(buffer, static_cast<uint64_t>(frame_type::new_token));
1032 append_varint(buffer, f.token.size());
1033 append_bytes(buffer, f.token);
1034 return buffer;
1035}
1036
1037auto frame_builder::build_stream(const stream_frame& f, bool include_length)
1038 -> std::vector<uint8_t>
1039{
1040 std::vector<uint8_t> buffer;
1041
1042 // Build frame type with flags
1043 uint8_t type = make_stream_type(f.fin, include_length, f.offset > 0);
1044 buffer.push_back(type);
1045
1046 // Stream ID
1047 append_varint(buffer, f.stream_id);
1048
1049 // Offset (if non-zero)
1050 if (f.offset > 0)
1051 {
1052 append_varint(buffer, f.offset);
1053 }
1054
1055 // Length (if requested)
1056 if (include_length)
1057 {
1058 append_varint(buffer, f.data.size());
1059 }
1060
1061 // Data
1062 append_bytes(buffer, f.data);
1063
1064 return buffer;
1065}
1066
1068 -> std::vector<uint8_t>
1069{
1070 std::vector<uint8_t> buffer;
1071 append_varint(buffer, static_cast<uint64_t>(frame_type::max_data));
1072 append_varint(buffer, f.maximum_data);
1073 return buffer;
1074}
1075
1077 -> std::vector<uint8_t>
1078{
1079 std::vector<uint8_t> buffer;
1080 append_varint(buffer, static_cast<uint64_t>(frame_type::max_stream_data));
1081 append_varint(buffer, f.stream_id);
1082 append_varint(buffer, f.maximum_stream_data);
1083 return buffer;
1084}
1085
1087 -> std::vector<uint8_t>
1088{
1089 std::vector<uint8_t> buffer;
1090 append_varint(buffer, f.bidirectional
1091 ? static_cast<uint64_t>(frame_type::max_streams_bidi)
1092 : static_cast<uint64_t>(frame_type::max_streams_uni));
1093 append_varint(buffer, f.maximum_streams);
1094 return buffer;
1095}
1096
1098 -> std::vector<uint8_t>
1099{
1100 std::vector<uint8_t> buffer;
1101 append_varint(buffer, static_cast<uint64_t>(frame_type::data_blocked));
1102 append_varint(buffer, f.maximum_data);
1103 return buffer;
1104}
1105
1107 -> std::vector<uint8_t>
1108{
1109 std::vector<uint8_t> buffer;
1110 append_varint(buffer, static_cast<uint64_t>(frame_type::stream_data_blocked));
1111 append_varint(buffer, f.stream_id);
1112 append_varint(buffer, f.maximum_stream_data);
1113 return buffer;
1114}
1115
1117 -> std::vector<uint8_t>
1118{
1119 std::vector<uint8_t> buffer;
1120 append_varint(buffer, f.bidirectional
1121 ? static_cast<uint64_t>(frame_type::streams_blocked_bidi)
1122 : static_cast<uint64_t>(frame_type::streams_blocked_uni));
1123 append_varint(buffer, f.maximum_streams);
1124 return buffer;
1125}
1126
1128 -> std::vector<uint8_t>
1129{
1130 std::vector<uint8_t> buffer;
1131 append_varint(buffer, static_cast<uint64_t>(frame_type::new_connection_id));
1132 append_varint(buffer, f.sequence_number);
1133 append_varint(buffer, f.retire_prior_to);
1134
1135 // Connection ID length (1 byte, not varint)
1136 buffer.push_back(static_cast<uint8_t>(f.connection_id.size()));
1137
1138 // Connection ID
1139 append_bytes(buffer, f.connection_id);
1140
1141 // Stateless Reset Token
1142 buffer.insert(buffer.end(), f.stateless_reset_token.begin(),
1143 f.stateless_reset_token.end());
1144
1145 return buffer;
1146}
1147
1149 -> std::vector<uint8_t>
1150{
1151 std::vector<uint8_t> buffer;
1152 append_varint(buffer, static_cast<uint64_t>(frame_type::retire_connection_id));
1153 append_varint(buffer, f.sequence_number);
1154 return buffer;
1155}
1156
1158 -> std::vector<uint8_t>
1159{
1160 std::vector<uint8_t> buffer;
1161 append_varint(buffer, static_cast<uint64_t>(frame_type::path_challenge));
1162 buffer.insert(buffer.end(), f.data.begin(), f.data.end());
1163 return buffer;
1164}
1165
1167 -> std::vector<uint8_t>
1168{
1169 std::vector<uint8_t> buffer;
1170 append_varint(buffer, static_cast<uint64_t>(frame_type::path_response));
1171 buffer.insert(buffer.end(), f.data.begin(), f.data.end());
1172 return buffer;
1173}
1174
1176 -> std::vector<uint8_t>
1177{
1178 std::vector<uint8_t> buffer;
1179 append_varint(buffer, f.is_application_error
1180 ? static_cast<uint64_t>(frame_type::connection_close_app)
1181 : static_cast<uint64_t>(frame_type::connection_close));
1182
1183 append_varint(buffer, f.error_code);
1184
1185 // Frame Type (transport close only)
1186 if (!f.is_application_error)
1187 {
1188 append_varint(buffer, f.frame_type);
1189 }
1190
1191 // Reason Phrase Length
1192 append_varint(buffer, f.reason_phrase.size());
1193
1194 // Reason Phrase
1195 buffer.insert(buffer.end(), f.reason_phrase.begin(), f.reason_phrase.end());
1196
1197 return buffer;
1198}
1199
1200auto frame_builder::build_handshake_done() -> std::vector<uint8_t>
1201{
1202 return {static_cast<uint8_t>(frame_type::handshake_done)};
1203}
1204
1205} // namespace kcenon::network::protocols::quic
static auto build_path_response(const path_response_frame &f) -> std::vector< uint8_t >
Build PATH_RESPONSE frame.
Definition frame.cpp:1166
static auto build_streams_blocked(const streams_blocked_frame &f) -> std::vector< uint8_t >
Build STREAMS_BLOCKED frame.
Definition frame.cpp:1116
static auto build_reset_stream(const reset_stream_frame &f) -> std::vector< uint8_t >
Build RESET_STREAM frame.
Definition frame.cpp:996
static auto build_stream(const stream_frame &f, bool include_length=true) -> std::vector< uint8_t >
Build STREAM frame.
Definition frame.cpp:1037
static auto build_stop_sending(const stop_sending_frame &f) -> std::vector< uint8_t >
Build STOP_SENDING frame.
Definition frame.cpp:1007
static auto build_ack(const ack_frame &f) -> std::vector< uint8_t >
Build ACK frame.
Definition frame.cpp:952
static auto build_padding(size_t count=1) -> std::vector< uint8_t >
Build PADDING frame.
Definition frame.cpp:942
static auto build_max_stream_data(const max_stream_data_frame &f) -> std::vector< uint8_t >
Build MAX_STREAM_DATA frame.
Definition frame.cpp:1076
static auto build_data_blocked(const data_blocked_frame &f) -> std::vector< uint8_t >
Build DATA_BLOCKED frame.
Definition frame.cpp:1097
static auto build_new_token(const new_token_frame &f) -> std::vector< uint8_t >
Build NEW_TOKEN frame.
Definition frame.cpp:1027
static auto build_max_data(const max_data_frame &f) -> std::vector< uint8_t >
Build MAX_DATA frame.
Definition frame.cpp:1067
static auto build_ping() -> std::vector< uint8_t >
Build PING frame.
Definition frame.cpp:947
static auto build_new_connection_id(const new_connection_id_frame &f) -> std::vector< uint8_t >
Build NEW_CONNECTION_ID frame.
Definition frame.cpp:1127
static auto build_crypto(const crypto_frame &f) -> std::vector< uint8_t >
Build CRYPTO frame.
Definition frame.cpp:1017
static auto build(const frame &f) -> std::vector< uint8_t >
Build any frame from variant.
Definition frame.cpp:893
static void append_bytes(std::vector< uint8_t > &buffer, std::span< const uint8_t > data)
Definition frame.cpp:887
static auto build_path_challenge(const path_challenge_frame &f) -> std::vector< uint8_t >
Build PATH_CHALLENGE frame.
Definition frame.cpp:1157
static auto build_connection_close(const connection_close_frame &f) -> std::vector< uint8_t >
Build CONNECTION_CLOSE frame.
Definition frame.cpp:1175
static auto build_handshake_done() -> std::vector< uint8_t >
Build HANDSHAKE_DONE frame.
Definition frame.cpp:1200
static void append_varint(std::vector< uint8_t > &buffer, uint64_t value)
Definition frame.cpp:881
static auto build_stream_data_blocked(const stream_data_blocked_frame &f) -> std::vector< uint8_t >
Build STREAM_DATA_BLOCKED frame.
Definition frame.cpp:1106
static auto build_retire_connection_id(const retire_connection_id_frame &f) -> std::vector< uint8_t >
Build RETIRE_CONNECTION_ID frame.
Definition frame.cpp:1148
static auto build_max_streams(const max_streams_frame &f) -> std::vector< uint8_t >
Build MAX_STREAMS frame.
Definition frame.cpp:1086
static auto parse_connection_close(std::span< const uint8_t > data, bool is_app) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:827
static auto parse_path_response(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:812
static auto parse_max_stream_data(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:646
static auto parse_ack(std::span< const uint8_t > data, bool has_ecn) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:394
static auto peek_type(std::span< const uint8_t > data) -> Result< std::pair< uint64_t, size_t > >
Get the frame type from raw data without full parsing.
Definition frame.cpp:116
static auto parse_ping(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:387
static auto parse_all(std::span< const uint8_t > data) -> Result< std::vector< frame > >
Parse all frames from buffer.
Definition frame.cpp:343
static auto parse_reset_stream(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:478
static auto parse_data_blocked(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:681
static auto parse_retire_connection_id(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:784
static auto parse_max_streams(std::span< const uint8_t > data, bool bidi) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:667
static auto parse_stream_data_blocked(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:694
static auto parse_stream(std::span< const uint8_t > data, uint8_t flags) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:578
static auto parse_path_challenge(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:797
static auto parse_padding(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:367
static auto parse_new_connection_id(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:729
static auto parse_crypto(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:526
static auto parse_stop_sending(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:505
static auto parse_new_token(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:555
static auto parse(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Parse a single frame from buffer.
Definition frame.cpp:122
static auto parse_max_data(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:633
static auto parse_handshake_done(std::span< const uint8_t > data) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:871
static auto parse_streams_blocked(std::span< const uint8_t > data, bool bidi) -> Result< std::pair< frame, size_t > >
Definition frame.cpp:715
static auto encode(uint64_t value) -> std::vector< uint8_t >
Encode a value to variable-length format.
Definition varint.cpp:10
static auto decode(std::span< const uint8_t > data) -> Result< std::pair< uint64_t, size_t > >
Decode variable-length integer from buffer.
Definition varint.cpp:146
uint32_t code
Definition hpack.cpp:668
constexpr uint8_t fin
Stream is finished.
Definition frame_types.h:63
constexpr uint8_t len
Length field present.
Definition frame_types.h:64
constexpr uint8_t off
Offset field present.
Definition frame_types.h:65
constexpr auto is_stream_frame(uint64_t type) noexcept -> bool
Check if a frame type value represents a STREAM frame.
Definition frame_types.h:73
@ error
Black hole detected, reset to base.
auto frame_type_to_string(frame_type type) -> std::string
Get string name for a frame type.
Definition frame.cpp:80
constexpr auto make_stream_type(bool has_fin, bool has_length, bool has_offset) noexcept -> uint8_t
Build STREAM frame type from flags.
Definition frame_types.h:89
frame_type
QUIC frame types as defined in RFC 9000 Section 12.4.
Definition frame_types.h:25
std::variant< padding_frame, ping_frame, ack_frame, reset_stream_frame, stop_sending_frame, crypto_frame, new_token_frame, stream_frame, max_data_frame, max_stream_data_frame, max_streams_frame, data_blocked_frame, stream_data_blocked_frame, streams_blocked_frame, new_connection_id_frame, retire_connection_id_frame, path_challenge_frame, path_response_frame, connection_close_frame, handshake_done_frame > frame
Variant type holding any QUIC frame.
auto get_frame_type(const frame &f) -> frame_type
Get the frame type for a frame variant.
Definition frame.cpp:28
constexpr auto get_stream_flags(uint64_t type) noexcept -> uint8_t
Extract STREAM flags from frame type.
Definition frame_types.h:81
VoidResult ok()
ACK frame (RFC 9000 Section 19.3)
uint64_t ack_delay
Time since receiving largest_acknowledged (encoded)
std::optional< ecn_counts > ecn
ECN counts (for ACK_ECN frames)
uint64_t largest_acknowledged
Largest packet number acknowledged.
std::vector< ack_range > ranges
Additional ACK ranges.
uint64_t gap
Number of contiguous unacknowledged packets.
uint64_t length
Number of contiguous acknowledged packets.
CONNECTION_CLOSE frame (RFC 9000 Section 19.19)
uint64_t error_code
Error code indicating reason.
bool is_application_error
True if application-level error.
uint64_t frame_type
Type of frame that triggered (transport close only)
CRYPTO frame (RFC 9000 Section 19.6)
uint64_t offset
Byte offset in crypto stream.
std::vector< uint8_t > data
Cryptographic handshake data.
DATA_BLOCKED frame (RFC 9000 Section 19.12)
uint64_t maximum_data
Connection-level limit at which blocking occurred.
ECN counts for ACK_ECN frames.
HANDSHAKE_DONE frame (RFC 9000 Section 19.20)
MAX_DATA frame (RFC 9000 Section 19.9)
uint64_t maximum_data
Maximum data that can be sent.
MAX_STREAM_DATA frame (RFC 9000 Section 19.10)
MAX_STREAMS frame (RFC 9000 Section 19.11)
uint64_t maximum_streams
Maximum number of streams.
bool bidirectional
True for bidi, false for uni.
NEW_CONNECTION_ID frame (RFC 9000 Section 19.15)
std::vector< uint8_t > connection_id
Connection ID (1-20 bytes)
uint64_t sequence_number
Sequence number for this CID.
std::array< uint8_t, 16 > stateless_reset_token
Stateless reset token.
uint64_t retire_prior_to
CIDs below this should be retired.
NEW_TOKEN frame (RFC 9000 Section 19.7)
std::vector< uint8_t > token
Opaque token.
PADDING frame (RFC 9000 Section 19.1)
PATH_CHALLENGE frame (RFC 9000 Section 19.17)
std::array< uint8_t, 8 > data
Arbitrary 8-byte data.
PATH_RESPONSE frame (RFC 9000 Section 19.18)
std::array< uint8_t, 8 > data
Data from PATH_CHALLENGE.
PING frame (RFC 9000 Section 19.2)
RESET_STREAM frame (RFC 9000 Section 19.4)
uint64_t application_error_code
Application error code.
RETIRE_CONNECTION_ID frame (RFC 9000 Section 19.16)
uint64_t sequence_number
Sequence number of CID to retire.
STOP_SENDING frame (RFC 9000 Section 19.5)
uint64_t application_error_code
Application error code.
STREAM_DATA_BLOCKED frame (RFC 9000 Section 19.13)
uint64_t maximum_stream_data
Stream-level limit at which blocking occurred.
STREAM frame (RFC 9000 Section 19.8)
std::vector< uint8_t > data
Stream data.
uint64_t offset
Byte offset in stream (0 if not present)
bool fin
True if this is the final data.
STREAMS_BLOCKED frame (RFC 9000 Section 19.14)
uint64_t maximum_streams
Stream limit at which blocking occurred.
bool bidirectional
True for bidi, false for uni.