Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
connection.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
11
12#include <algorithm>
13
15{
16
17// ============================================================================
18// String Conversion Functions
19// ============================================================================
20
22{
23 switch (state)
24 {
26 return "idle";
28 return "handshaking";
30 return "connected";
32 return "closing";
34 return "draining";
36 return "closed";
37 default:
38 return "unknown";
39 }
40}
41
43{
44 switch (state)
45 {
47 return "initial";
49 return "waiting_server_hello";
51 return "waiting_finished";
53 return "complete";
54 default:
55 return "unknown";
56 }
57}
58
59// ============================================================================
60// Constructor / Destructor
61// ============================================================================
62
63connection::connection(bool is_server, const connection_id& initial_dcid)
64 : is_server_(is_server)
65 , initial_dcid_(initial_dcid)
66 , peer_cid_manager_(8) // Default active_connection_id_limit
67 , stream_mgr_(is_server)
68 , flow_ctrl_(1048576) // 1MB default
69 , loss_detector_(rtt_estimator_)
70{
71 // Generate local connection ID
73 local_cids_.emplace_back(0, local_cid_);
74
75 // For client: remote CID is the initial DCID
76 // For server: remote CID will be set when we receive client's SCID
77 if (!is_server)
78 {
81 }
82
83 // Initialize default transport parameters
84 if (is_server)
85 {
87 }
88 else
89 {
91 }
92
93 // Set initial source connection ID
95
96 // Initialize idle timer
98}
99
100connection::~connection() = default;
101
102// Move operations are deleted because some members are not movable
103
104// ============================================================================
105// Connection IDs
106// ============================================================================
107
108auto connection::add_local_cid(const connection_id& cid, uint64_t sequence)
109 -> VoidResult
110{
111 // Check for duplicates
112 for (const auto& [seq, existing_cid] : local_cids_)
113 {
114 if (seq == sequence)
115 {
117 "Duplicate connection ID sequence",
118 "connection");
119 }
120 }
121
122 local_cids_.emplace_back(sequence, cid);
123 return ok();
124}
125
126auto connection::retire_cid(uint64_t sequence) -> VoidResult
127{
128 auto it = std::find_if(local_cids_.begin(), local_cids_.end(),
129 [sequence](const auto& p) { return p.first == sequence; });
130
131 if (it == local_cids_.end())
132 {
134 "Connection ID not found",
135 "connection");
136 }
137
138 // Don't retire the only remaining CID
139 if (local_cids_.size() <= 1)
140 {
142 "Cannot retire last connection ID",
143 "connection");
144 }
145
146 local_cids_.erase(it);
147 return ok();
148}
149
150// ============================================================================
151// Transport Parameters
152// ============================================================================
153
163
169
171{
172 // Apply to flow control
174
175 // Apply to stream manager
178
179 // Apply to peer CID manager
181
182 // Update idle timeout
184 {
185 auto timeout = std::min(local_params_.max_idle_timeout,
187 if (timeout > 0)
188 {
189 idle_deadline_ = std::chrono::steady_clock::now() +
190 std::chrono::milliseconds(timeout);
191 }
192 }
193}
194
195// ============================================================================
196// Handshake
197// ============================================================================
198
199auto connection::start_handshake(const std::string& server_name)
201{
202 auto span = tracing::is_tracing_enabled()
203 ? std::make_optional(tracing::trace_context::create_span("quic.connection.start_handshake"))
204 : std::nullopt;
205 if (span)
206 {
207 span->set_attribute("quic.role", "client")
208 .set_attribute("quic.server_name", server_name)
209 .set_attribute("net.transport", "udp")
210 .set_attribute("quic.version", "1");
211 }
212
213 if (is_server_)
214 {
215 if (span)
216 {
217 span->set_error("Server cannot start handshake");
218 }
220 "Server cannot start handshake",
221 "connection");
222 }
223
224 if (state_ != connection_state::idle)
225 {
226 if (span)
227 {
228 span->set_error("Connection already started");
229 }
231 "Connection already started",
232 "connection");
233 }
234
235 // Initialize crypto for client
236 auto init_result = crypto_.init_client(server_name);
237 if (init_result.is_err())
238 {
239 if (span)
240 {
241 span->set_error(init_result.error().message);
242 }
244 "Failed to initialize crypto",
245 "connection",
246 init_result.error().message);
247 }
248
249 // Derive initial secrets
250 auto derive_result = crypto_.derive_initial_secrets(initial_dcid_);
251 if (derive_result.is_err())
252 {
253 if (span)
254 {
255 span->set_error(derive_result.error().message);
256 }
258 "Failed to derive initial secrets",
259 "connection",
260 derive_result.error().message);
261 }
262
263 // Start TLS handshake to get ClientHello
264 auto hs_result = crypto_.start_handshake();
265 if (hs_result.is_err())
266 {
267 if (span)
268 {
269 span->set_error(hs_result.error().message);
270 }
272 "Failed to start TLS handshake",
273 "connection",
274 hs_result.error().message);
275 }
276
277 // Store crypto data for Initial packet
278 if (!hs_result.value().empty())
279 {
280 pending_crypto_initial_.push_back(std::move(hs_result.value()));
281 }
282
283 // Update state
286
287 if (span)
288 {
289 span->set_attribute("quic.state", "handshaking");
290 }
291
292 return ok(std::vector<uint8_t>{});
293}
294
295auto connection::init_server_handshake(const std::string& cert_file,
296 const std::string& key_file) -> VoidResult
297{
298 auto span = tracing::is_tracing_enabled()
299 ? std::make_optional(tracing::trace_context::create_span("quic.connection.init_server_handshake"))
300 : std::nullopt;
301 if (span)
302 {
303 span->set_attribute("quic.role", "server")
304 .set_attribute("net.transport", "udp")
305 .set_attribute("quic.version", "1");
306 }
307
308 if (!is_server_)
309 {
310 if (span)
311 {
312 span->set_error("Not a server connection");
313 }
315 "Not a server connection",
316 "connection");
317 }
318
319 auto result = crypto_.init_server(cert_file, key_file);
320 if (result.is_err())
321 {
322 if (span)
323 {
324 span->set_error(result.error().message);
325 }
327 "Failed to initialize server crypto",
328 "connection",
329 result.error().message);
330 }
331
332 // Derive initial secrets using the client's DCID
333 auto derive_result = crypto_.derive_initial_secrets(initial_dcid_);
334 if (derive_result.is_err())
335 {
336 if (span)
337 {
338 span->set_error(derive_result.error().message);
339 }
341 "Failed to derive initial secrets",
342 "connection",
343 derive_result.error().message);
344 }
345
347 if (span)
348 {
349 span->set_attribute("quic.state", "handshaking");
350 }
351 return ok();
352}
353
354// ============================================================================
355// Packet Processing
356// ============================================================================
357
358auto connection::receive_packet(std::span<const uint8_t> data) -> VoidResult
359{
360 auto span = tracing::is_tracing_enabled()
361 ? std::make_optional(tracing::trace_context::create_span("quic.connection.receive_packet"))
362 : std::nullopt;
363 if (span)
364 {
365 span->set_attribute("quic.packet.size", static_cast<int64_t>(data.size()))
366 .set_attribute("quic.role", is_server_ ? "server" : "client")
367 .set_attribute("quic.state", connection_state_to_string(state_));
368 }
369
370 if (data.empty())
371 {
372 if (span)
373 {
374 span->set_error("Empty packet");
375 }
377 "Empty packet",
378 "connection");
379 }
380
381 if (is_draining() || is_closed())
382 {
383 // In draining/closed state, just count the packet
384 packets_received_++;
385 bytes_received_ += data.size();
386 if (span)
387 {
388 span->set_attribute("quic.packet.ignored", true);
389 }
390 return ok();
391 }
392
393 bytes_received_ += data.size();
394 packets_received_++;
395 reset_idle_timer();
396
397 // Check header form
399 {
400 auto parse_result = packet_parser::parse_long_header(data);
401 if (parse_result.is_err())
402 {
404 "Failed to parse long header",
405 "connection",
406 parse_result.error().message);
407 }
408
409 auto [header, header_len] = parse_result.value();
410 return process_long_header_packet(header, data.subspan(header_len));
411 }
412 else
413 {
414 auto parse_result = packet_parser::parse_short_header(data, local_cid_.length());
415 if (parse_result.is_err())
416 {
418 "Failed to parse short header",
419 "connection",
420 parse_result.error().message);
421 }
422
423 auto [header, header_len] = parse_result.value();
424 return process_short_header_packet(header, data.subspan(header_len));
425 }
426}
427
429 std::span<const uint8_t> payload)
430 -> VoidResult
431{
432 encryption_level level;
433 switch (hdr.type())
434 {
437 pending_ack_initial_ = true;
438 break;
441 pending_ack_handshake_ = true;
442 break;
445 break;
447 // Handle Retry packet specially
448 return ok();
449 default:
451 "Unknown packet type",
452 "connection");
453 }
454
455 // Update remote CID if this is first packet from peer
456 if (remote_cid_.length() == 0)
457 {
458 remote_cid_ = hdr.src_conn_id;
459 peer_cid_manager_.set_initial_peer_cid(hdr.src_conn_id);
460 }
461
462 // Server: set original DCID for transport parameters
463 if (is_server_ && !local_params_.original_destination_connection_id)
464 {
465 local_params_.original_destination_connection_id = hdr.dest_conn_id;
466 }
467
468 // Get keys for this level
469 auto keys_result = crypto_.get_read_keys(level);
470 if (keys_result.is_err())
471 {
472 // Keys not yet available - this might happen during handshake
473 return ok();
474 }
475
476 // Track largest packet number
477 auto& space = get_pn_space(level);
478 if (hdr.packet_number > space.largest_received)
479 {
480 space.largest_received = hdr.packet_number;
481 space.largest_received_time = std::chrono::steady_clock::now();
482 }
483 space.ack_needed = true;
484
485 return process_frames(payload, level);
486}
487
489 std::span<const uint8_t> payload)
490 -> VoidResult
491{
492 if (state_ != connection_state::connected)
493 {
494 // Short header packets require completed handshake
496 "Received 1-RTT packet before handshake complete",
497 "connection");
498 }
499
500 pending_ack_app_ = true;
501
502 // Get 1-RTT keys
503 auto keys_result = crypto_.get_read_keys(encryption_level::application);
504 if (keys_result.is_err())
505 {
507 "1-RTT keys not available",
508 "connection");
509 }
510
511 // Track largest packet number
512 auto& space = app_space_;
513 if (hdr.packet_number > space.largest_received)
514 {
515 space.largest_received = hdr.packet_number;
516 space.largest_received_time = std::chrono::steady_clock::now();
517 }
518 space.ack_needed = true;
519
520 return process_frames(payload, encryption_level::application);
521}
522
523auto connection::process_frames(std::span<const uint8_t> payload,
525{
526 // Parse all frames in the payload
527 auto frames_result = frame_parser::parse_all(payload);
528 if (frames_result.is_err())
529 {
531 "Failed to parse frames",
532 "connection",
533 frames_result.error().message);
534 }
535
536 // Process each frame
537 for (const auto& f : frames_result.value())
538 {
539 auto result = handle_frame(f, level);
540 if (result.is_err())
541 {
542 return result;
543 }
544 }
545
546 update_state();
547 return ok();
548}
549
551 -> VoidResult
552{
553 return std::visit(
554 [this, level](const auto& f) -> VoidResult
555 {
556 using T = std::decay_t<decltype(f)>;
557
558 if constexpr (std::is_same_v<T, padding_frame>)
559 {
560 // Padding is ignored
561 return ok();
562 }
563 else if constexpr (std::is_same_v<T, ping_frame>)
564 {
565 // Ping just elicits an ACK (already handled)
566 return ok();
567 }
568 else if constexpr (std::is_same_v<T, ack_frame>)
569 {
570 // Process ACK using loss detector (RFC 9002)
571 auto recv_time = std::chrono::steady_clock::now();
572 auto result = loss_detector_.on_ack_received(f, level, recv_time);
573
574 // Handle loss detection result
575 handle_loss_detection_result(result);
576
577 // Reset PTO count on receiving acknowledgment
578 loss_detector_.reset_pto_count();
579
580 // Update handshake confirmation for loss detector
581 if (crypto_.is_handshake_complete())
582 {
583 loss_detector_.set_handshake_confirmed(true);
584 }
585
586 // Also update our local packet number space tracking
587 auto& space = get_pn_space(level);
588 if (f.largest_acknowledged > space.largest_acked)
589 {
590 space.largest_acked = f.largest_acknowledged;
591 }
592
593 // Remove acknowledged packets from local tracking
594 for (auto it = space.sent_packets.begin();
595 it != space.sent_packets.end();)
596 {
597 if (it->first <= f.largest_acknowledged)
598 {
599 it = space.sent_packets.erase(it);
600 }
601 else
602 {
603 ++it;
604 }
605 }
606 return ok();
607 }
608 else if constexpr (std::is_same_v<T, crypto_frame>)
609 {
610 // Process CRYPTO frame
611 auto result = crypto_.process_crypto_data(level,
612 std::span<const uint8_t>(f.data.data(), f.data.size()));
613 if (result.is_err())
614 {
616 "Failed to process crypto data",
617 "connection",
618 result.error().message);
619 }
620
621 // Store response crypto data
622 if (!result.value().empty())
623 {
624 switch (level)
625 {
627 pending_crypto_initial_.push_back(std::move(result.value()));
628 break;
630 pending_crypto_handshake_.push_back(std::move(result.value()));
631 break;
632 default:
633 pending_crypto_app_.push_back(std::move(result.value()));
634 break;
635 }
636 }
637
638 return ok();
639 }
640 else if constexpr (std::is_same_v<T, stream_frame>)
641 {
642 // Get or create stream
643 auto stream_result = stream_mgr_.get_or_create_stream(f.stream_id);
644 if (stream_result.is_err())
645 {
647 "Failed to get stream",
648 "connection",
649 stream_result.error().message);
650 }
651
652 // Deliver data
653 auto recv_result = stream_result.value()->receive_data(
654 f.offset,
655 std::span<const uint8_t>(f.data.data(), f.data.size()),
656 f.fin);
657 if (recv_result.is_err())
658 {
659 return recv_result;
660 }
661
662 // Update connection-level flow control
663 auto recv_flow = flow_ctrl_.record_received(f.data.size());
664 if (recv_flow.is_err())
665 {
666 return recv_flow;
667 }
668 return ok();
669 }
670 else if constexpr (std::is_same_v<T, max_data_frame>)
671 {
672 flow_ctrl_.update_send_limit(f.maximum_data);
673 return ok();
674 }
675 else if constexpr (std::is_same_v<T, max_stream_data_frame>)
676 {
677 auto* s = stream_mgr_.get_stream(f.stream_id);
678 if (s)
679 {
680 s->set_max_send_data(f.maximum_stream_data);
681 }
682 return ok();
683 }
684 else if constexpr (std::is_same_v<T, max_streams_frame>)
685 {
686 if (f.bidirectional)
687 {
688 stream_mgr_.set_peer_max_streams_bidi(f.maximum_streams);
689 }
690 else
691 {
692 stream_mgr_.set_peer_max_streams_uni(f.maximum_streams);
693 }
694 return ok();
695 }
696 else if constexpr (std::is_same_v<T, connection_close_frame>)
697 {
698 close_error_code_ = f.error_code;
699 close_reason_ = f.reason_phrase;
700 close_received_ = true;
701 application_close_ = f.is_application_error;
702 enter_draining();
703 return ok();
704 }
705 else if constexpr (std::is_same_v<T, handshake_done_frame>)
706 {
707 if (!is_server_)
708 {
709 hs_state_ = handshake_state::complete;
711 }
712 return ok();
713 }
714 else if constexpr (std::is_same_v<T, reset_stream_frame>)
715 {
716 auto* s = stream_mgr_.get_stream(f.stream_id);
717 if (s)
718 {
719 return s->receive_reset(f.application_error_code, f.final_size);
720 }
721 return ok();
722 }
723 else if constexpr (std::is_same_v<T, stop_sending_frame>)
724 {
725 auto* s = stream_mgr_.get_stream(f.stream_id);
726 if (s)
727 {
728 return s->receive_stop_sending(f.application_error_code);
729 }
730 return ok();
731 }
732 else if constexpr (std::is_same_v<T, new_connection_id_frame>)
733 {
734 // RFC 9000 Section 19.15: Process NEW_CONNECTION_ID frame
735 // Store the new peer connection ID with its metadata
736 connection_id cid(std::span<const uint8_t>(
737 f.connection_id.data(), f.connection_id.size()));
738
739 auto result = peer_cid_manager_.add_peer_cid(
740 cid,
741 f.sequence_number,
742 f.retire_prior_to,
743 f.stateless_reset_token);
744
745 if (result.is_err())
746 {
748 "Failed to process NEW_CONNECTION_ID",
749 "connection",
750 result.error().message);
751 }
752 return ok();
753 }
754 else if constexpr (std::is_same_v<T, retire_connection_id_frame>)
755 {
756 return retire_cid(f.sequence_number);
757 }
758 else if constexpr (std::is_same_v<T, data_blocked_frame> ||
759 std::is_same_v<T, stream_data_blocked_frame> ||
760 std::is_same_v<T, streams_blocked_frame> ||
761 std::is_same_v<T, path_challenge_frame> ||
762 std::is_same_v<T, path_response_frame> ||
763 std::is_same_v<T, new_token_frame>)
764 {
765 // These frames are acknowledged but may not require action
766 return ok();
767 }
768 else
769 {
770 return ok();
771 }
772 },
773 frm);
774}
775
776// ============================================================================
777// Packet Generation
778// ============================================================================
779
780auto connection::generate_packets() -> std::vector<std::vector<uint8_t>>
781{
782 std::vector<std::vector<uint8_t>> packets;
783
784 if (is_closed())
785 {
786 return packets;
787 }
788
789 // Generate close packet if closing
790 if (close_sent_ && !close_received_)
791 {
792 auto pkt = build_packet(encryption_level::application);
793 if (!pkt.empty())
794 {
795 packets.push_back(std::move(pkt));
796 }
797 return packets;
798 }
799
800 // Generate Initial packets if needed
801 if (!pending_crypto_initial_.empty() || pending_ack_initial_)
802 {
803 auto pkt = build_packet(encryption_level::initial);
804 if (!pkt.empty())
805 {
806 packets.push_back(std::move(pkt));
807 pending_ack_initial_ = false;
808 }
809 }
810
811 // Generate Handshake packets if needed
812 if (!pending_crypto_handshake_.empty() || pending_ack_handshake_)
813 {
814 auto pkt = build_packet(encryption_level::handshake);
815 if (!pkt.empty())
816 {
817 packets.push_back(std::move(pkt));
818 pending_ack_handshake_ = false;
819 }
820 }
821
822 // Generate 1-RTT packets if connected
823 if (state_ == connection_state::connected)
824 {
825 // Send HANDSHAKE_DONE (server only, once)
826 if (is_server_ && hs_state_ == handshake_state::complete)
827 {
828 // Will be included in next packet
829 }
830
831 // Generate application data packets
832 if (has_pending_data() || pending_ack_app_)
833 {
834 auto pkt = build_packet(encryption_level::application);
835 if (!pkt.empty())
836 {
837 packets.push_back(std::move(pkt));
838 pending_ack_app_ = false;
839 }
840 }
841 }
842
843 return packets;
844}
845
846auto connection::build_packet(encryption_level level) -> std::vector<uint8_t>
847{
848 std::vector<uint8_t> packet;
849
850 // Get keys for this level
851 auto keys_result = crypto_.get_write_keys(level);
852 if (keys_result.is_err())
853 {
854 return packet;
855 }
856
857 auto& space = get_pn_space(level);
858 uint64_t pn = space.next_pn++;
859
860 // Build header
861 std::vector<uint8_t> header;
862 switch (level)
863 {
866 remote_cid_, local_cid_, {}, pn);
867 break;
870 remote_cid_, local_cid_, pn);
871 break;
874 remote_cid_, pn, crypto_.key_phase());
875 break;
876 default:
877 return packet;
878 }
879
880 // Build payload with frames
881 std::vector<uint8_t> payload;
882
883 // Add ACK frame if needed
884 if (space.ack_needed)
885 {
886 auto ack = generate_ack_frame(space);
887 if (ack)
888 {
889 auto encoded = frame_builder::build_ack(*ack);
890 payload.insert(payload.end(), encoded.begin(), encoded.end());
891 space.ack_needed = false;
892 }
893 }
894
895 // Add CRYPTO frames
896 std::deque<std::vector<uint8_t>>* crypto_queue = nullptr;
897 switch (level)
898 {
900 crypto_queue = &pending_crypto_initial_;
901 break;
903 crypto_queue = &pending_crypto_handshake_;
904 break;
905 default:
906 crypto_queue = &pending_crypto_app_;
907 break;
908 }
909
910 uint64_t crypto_offset = 0;
911 while (!crypto_queue->empty())
912 {
913 auto& data = crypto_queue->front();
914 crypto_frame cf;
915 cf.offset = crypto_offset;
916 cf.data = std::move(data);
917 crypto_offset += cf.data.size();
918
919 auto encoded = frame_builder::build_crypto(cf);
920 payload.insert(payload.end(), encoded.begin(), encoded.end());
921 crypto_queue->pop_front();
922 }
923
924 // Add HANDSHAKE_DONE for server
925 if (level == encryption_level::application && is_server_ &&
927 {
929 auto encoded = frame_builder::build(hd);
930 payload.insert(payload.end(), encoded.begin(), encoded.end());
931 }
932
933 // Add CONNECTION_CLOSE if closing
934 if (close_sent_)
935 {
937 ccf.error_code = close_error_code_.value_or(0);
938 ccf.reason_phrase = close_reason_;
939 ccf.is_application_error = application_close_;
940 auto encoded = frame_builder::build(ccf);
941 payload.insert(payload.end(), encoded.begin(), encoded.end());
942 }
943
944 // Add RETIRE_CONNECTION_ID frames for application level
945 if (level == encryption_level::application && !close_sent_)
946 {
947 auto retire_frames = peer_cid_manager_.get_pending_retire_frames();
948 for (const auto& rf : retire_frames)
949 {
950 auto encoded = frame_builder::build(rf);
951 payload.insert(payload.end(), encoded.begin(), encoded.end());
952 }
953 if (!retire_frames.empty())
954 {
955 peer_cid_manager_.clear_pending_retire_frames();
956 }
957 }
958
959 // Add pending frames (e.g., PING from PTO probing)
960 if (!close_sent_)
961 {
962 while (!pending_frames_.empty())
963 {
964 const auto& f = pending_frames_.front();
965 auto encoded = frame_builder::build(f);
966 payload.insert(payload.end(), encoded.begin(), encoded.end());
967 pending_frames_.pop_front();
968 }
969 }
970
971 // Add stream data frames for application level
972 if (level == encryption_level::application && !close_sent_)
973 {
974 auto streams = stream_mgr_.streams_with_pending_data();
975 for (auto* s : streams)
976 {
977 if (auto sf = s->next_stream_frame(1200))
978 {
979 auto encoded = frame_builder::build_stream(*sf);
980 payload.insert(payload.end(), encoded.begin(), encoded.end());
981 }
982 }
983 }
984
985 if (payload.empty())
986 {
987 return packet;
988 }
989
990 // Add padding for Initial packets to meet minimum size
991 if (level == encryption_level::initial)
992 {
993 size_t min_size = 1200;
994 size_t current_size = header.size() + payload.size() + 16; // +16 for AEAD tag
995 if (current_size < min_size)
996 {
997 auto encoded = frame_builder::build_padding(min_size - current_size);
998 payload.insert(payload.end(), encoded.begin(), encoded.end());
999 }
1000 }
1001
1002 // Encrypt and protect the packet
1003 auto protect_result = packet_protection::protect(
1004 keys_result.value(),
1005 std::span<const uint8_t>(header.data(), header.size()),
1006 std::span<const uint8_t>(payload.data(), payload.size()),
1007 pn);
1008
1009 if (protect_result.is_err())
1010 {
1011 return {};
1012 }
1013
1014 packet = std::move(protect_result.value());
1015
1016 // Track sent packet for local state
1017 sent_packet_info info;
1018 info.packet_number = pn;
1019 info.sent_time = std::chrono::steady_clock::now();
1020 info.sent_bytes = packet.size();
1021 info.ack_eliciting = !payload.empty();
1022 info.in_flight = true;
1023 info.level = level;
1024 // Note: frames are not stored here as the current implementation
1025 // doesn't track individual frames in build_packet.
1026 // For full retransmission support, frames would need to be collected
1027 // during packet building and stored here.
1028
1029 // Notify loss detector about sent packet (RFC 9002)
1030 sent_packet sent_pkt;
1031 sent_pkt.packet_number = info.packet_number;
1032 sent_pkt.sent_time = info.sent_time;
1033 sent_pkt.sent_bytes = info.sent_bytes;
1034 sent_pkt.ack_eliciting = info.ack_eliciting;
1035 sent_pkt.in_flight = info.in_flight;
1036 sent_pkt.level = info.level;
1037 loss_detector_.on_packet_sent(sent_pkt);
1038
1039 // Notify congestion controller about sent packet
1040 if (info.in_flight)
1041 {
1042 congestion_controller_.on_packet_sent(info.sent_bytes);
1043 }
1044
1045 space.sent_packets[pn] = std::move(info);
1046
1047 bytes_sent_ += packet.size();
1048 packets_sent_++;
1049
1050 return packet;
1051}
1052
1054{
1055 if (!pending_crypto_initial_.empty() ||
1056 !pending_crypto_handshake_.empty() ||
1057 !pending_crypto_app_.empty())
1058 {
1059 return true;
1060 }
1061
1063 {
1064 return true;
1065 }
1066
1067 if (close_sent_)
1068 {
1069 return true;
1070 }
1071
1072 // Check for pending frames (e.g., PING from PTO probing)
1073 if (!pending_frames_.empty())
1074 {
1075 return true;
1076 }
1077
1078 // Check for stream data
1079 auto& streams = const_cast<stream_manager&>(stream_mgr_);
1080 return !streams.streams_with_pending_data().empty();
1081}
1082
1083// ============================================================================
1084// Packet Number Space Management
1085// ============================================================================
1086
1088{
1089 switch (level)
1090 {
1093 return initial_space_;
1095 return handshake_space_;
1096 default:
1097 return app_space_;
1098 }
1099}
1100
1102 -> const packet_number_space&
1103{
1104 switch (level)
1105 {
1108 return initial_space_;
1110 return handshake_space_;
1111 default:
1112 return app_space_;
1113 }
1114}
1115
1116// ============================================================================
1117// State Management
1118// ============================================================================
1119
1121{
1123 {
1124 hs_state_ = handshake_state::complete;
1125 if (is_server_)
1126 {
1128 }
1129 // Client waits for HANDSHAKE_DONE
1130 }
1131}
1132
1133// ============================================================================
1134// Connection Close
1135// ============================================================================
1136
1137auto connection::close(uint64_t error_code, const std::string& reason) -> VoidResult
1138{
1139 auto span = tracing::is_tracing_enabled()
1140 ? std::make_optional(tracing::trace_context::create_span("quic.connection.close"))
1141 : std::nullopt;
1142 if (span)
1143 {
1144 span->set_attribute("quic.close.error_code", static_cast<int64_t>(error_code))
1145 .set_attribute("quic.close.reason", reason)
1146 .set_attribute("quic.close.type", "transport")
1147 .set_attribute("quic.role", is_server_ ? "server" : "client");
1148 }
1149
1150 // Already closing or closed - don't update error state
1151 if (is_closed() || is_draining())
1152 {
1153 if (span)
1154 {
1155 span->set_attribute("quic.close.already_closing", true);
1156 }
1157 return ok();
1158 }
1159
1160 close_error_code_ = error_code;
1161 close_reason_ = reason;
1162 close_sent_ = true;
1163 application_close_ = false;
1164 enter_closing();
1165
1166 if (span)
1167 {
1168 span->set_attribute("quic.state", "closing");
1169 }
1170
1171 return ok();
1172}
1173
1174auto connection::close_application(uint64_t error_code, const std::string& reason)
1175 -> VoidResult
1176{
1177 auto span = tracing::is_tracing_enabled()
1178 ? std::make_optional(tracing::trace_context::create_span("quic.connection.close_application"))
1179 : std::nullopt;
1180 if (span)
1181 {
1182 span->set_attribute("quic.close.error_code", static_cast<int64_t>(error_code))
1183 .set_attribute("quic.close.reason", reason)
1184 .set_attribute("quic.close.type", "application")
1185 .set_attribute("quic.role", is_server_ ? "server" : "client");
1186 }
1187
1188 // Already closing or closed - don't update error state
1189 if (is_closed() || is_draining())
1190 {
1191 if (span)
1192 {
1193 span->set_attribute("quic.close.already_closing", true);
1194 }
1195 return ok();
1196 }
1197
1198 close_error_code_ = error_code;
1199 close_reason_ = reason;
1200 close_sent_ = true;
1201 application_close_ = true;
1202 enter_closing();
1203
1204 if (span)
1205 {
1206 span->set_attribute("quic.state", "closing");
1207 }
1208
1209 return ok();
1210}
1211
1213{
1215 {
1216 return;
1217 }
1218
1220
1221 // Set drain timer (3 * PTO)
1222 auto pto = std::chrono::milliseconds(100); // Simplified PTO
1223 drain_deadline_ = std::chrono::steady_clock::now() + 3 * pto;
1224}
1225
1227{
1229 {
1230 return;
1231 }
1232
1234
1235 // Set drain timer (3 * PTO)
1236 auto pto = std::chrono::milliseconds(100);
1237 drain_deadline_ = std::chrono::steady_clock::now() + 3 * pto;
1238}
1239
1240// ============================================================================
1241// Timers
1242// ============================================================================
1243
1245{
1246 auto timeout = local_params_.max_idle_timeout;
1248 {
1249 timeout = std::min(timeout, remote_params_.max_idle_timeout);
1250 }
1251
1252 if (timeout > 0)
1253 {
1254 idle_deadline_ = std::chrono::steady_clock::now() +
1255 std::chrono::milliseconds(timeout);
1256 }
1257}
1258
1260 -> std::optional<std::chrono::steady_clock::time_point>
1261{
1262 if (is_closed())
1263 {
1264 return std::nullopt;
1265 }
1266
1267 if (is_draining())
1268 {
1269 return drain_deadline_;
1270 }
1271
1272 // Return the earliest of idle timeout and loss detection timeout
1273 auto loss_timeout = loss_detector_.next_timeout();
1274 if (loss_timeout.has_value())
1275 {
1276 return std::min(idle_deadline_, *loss_timeout);
1277 }
1278
1279 return idle_deadline_;
1280}
1281
1283{
1284 auto now = std::chrono::steady_clock::now();
1285
1286 // Check drain timeout
1287 if (is_draining() && now >= drain_deadline_)
1288 {
1290 return;
1291 }
1292
1293 // Check idle timeout
1294 if (now >= idle_deadline_)
1295 {
1297 close_reason_ = "Idle timeout";
1299 return;
1300 }
1301
1302 // Handle PTO timeout for loss detection (RFC 9002 Section 6.2)
1303 auto loss_timeout = loss_detector_.next_timeout();
1304 if (loss_timeout.has_value() && now >= *loss_timeout)
1305 {
1306 auto result = loss_detector_.on_timeout();
1308 }
1309}
1310
1311// ============================================================================
1312// ACK Generation
1313// ============================================================================
1314
1316 -> std::optional<ack_frame>
1317{
1318 if (space.largest_received == 0 && !space.ack_needed)
1319 {
1320 return std::nullopt;
1321 }
1322
1323 ack_frame ack;
1324 ack.largest_acknowledged = space.largest_received;
1325 ack.ack_delay = std::chrono::duration_cast<std::chrono::microseconds>(
1326 std::chrono::steady_clock::now() - space.largest_received_time)
1327 .count();
1328 // Simplified: ACK all packets from 0 to largest_received
1329 // The first ACK range is implicit (largest_acknowledged to largest_acknowledged - first_ack_range_length)
1330 // For simplicity, we don't add any additional ranges
1331
1332 return ack;
1333}
1334
1335// ============================================================================
1336// Loss Detection and Congestion Control (RFC 9002)
1337// ============================================================================
1338
1340{
1341 switch (result.event)
1342 {
1344 // PTO timeout: generate probe packets (RFC 9002 Section 6.2.4)
1346 break;
1347
1349 // Handle lost packets: queue frames for retransmission and update congestion control
1350 for (const auto& lost : result.lost_packets)
1351 {
1354 }
1355 break;
1356
1358 // No action needed
1359 break;
1360 }
1361
1362 // Process acknowledged packets for congestion control
1363 auto ack_time = std::chrono::steady_clock::now();
1364 for (const auto& acked : result.acked_packets)
1365 {
1366 congestion_controller_.on_packet_acked(acked, ack_time);
1367 }
1368
1369 // Handle ECN congestion signal (RFC 9002 Section 7.1)
1371 {
1372 // ECN-CE marks indicate congestion without packet loss
1373 // Trigger congestion response using the sent time of the triggering packet
1375 }
1376 else if (result.ecn_signal == ecn_result::ecn_failure)
1377 {
1378 // ECN validation failed - disable ECN for this connection
1379 // The ecn_tracker in loss_detector already handles this internally
1380 // No additional action needed here
1381 }
1382}
1383
1385{
1386 // RFC 9002 Section 6.2.4: When PTO expires, send one or two ack-eliciting packets.
1387 // Probe packets must be ack-eliciting.
1388
1389 // Determine which encryption level to use for probes
1390 // Priority: Application > Handshake > Initial (use the highest available)
1392
1394 {
1395 probe_level = encryption_level::application;
1396 }
1398 {
1399 probe_level = encryption_level::handshake;
1400 }
1401
1402 // Generate a PING frame as an ack-eliciting probe
1403 // PING frames are the simplest ack-eliciting frames
1404 pending_frames_.emplace_back(ping_frame{});
1405
1406 // Mark that we need to send packets
1407 switch (probe_level)
1408 {
1410 // For initial space, we might need to retransmit crypto data
1411 if (!pending_crypto_initial_.empty())
1412 {
1413 // Crypto data will be sent as probe
1414 break;
1415 }
1416 // Queue a PING frame
1417 break;
1418
1420 if (!pending_crypto_handshake_.empty())
1421 {
1422 break;
1423 }
1424 break;
1425
1428 // Application level: PING is sufficient
1429 break;
1430 }
1431}
1432
1434{
1435 // Queue frames from the lost packet for retransmission
1436 // Note: Some frames should not be retransmitted (ACK, PADDING)
1437 const auto level = lost_packet.level;
1438
1439 for (const auto& f : lost_packet.frames)
1440 {
1441 std::visit(
1442 [this, level](const auto& frm)
1443 {
1444 using T = std::decay_t<decltype(frm)>;
1445
1446 // Skip non-retransmittable frames
1447 if constexpr (std::is_same_v<T, padding_frame> ||
1448 std::is_same_v<T, ack_frame>)
1449 {
1450 // These frames are not retransmitted
1451 }
1452 else if constexpr (std::is_same_v<T, crypto_frame>)
1453 {
1454 // Crypto frames need to be retransmitted with their data
1455 switch (level)
1456 {
1458 pending_crypto_initial_.push_back(frm.data);
1459 break;
1461 pending_crypto_handshake_.push_back(frm.data);
1462 break;
1465 pending_crypto_app_.push_back(frm.data);
1466 break;
1467 }
1468 }
1469 else if constexpr (std::is_same_v<T, stream_frame>)
1470 {
1471 // Stream data retransmission:
1472 // The stream will naturally resend unacknowledged data
1473 // when next_stream_frame() is called, as the data remains
1474 // in the send buffer until acknowledged via acknowledge_data().
1475 // No explicit action needed here.
1476 (void)frm;
1477 }
1478 else if constexpr (std::is_same_v<T, ping_frame> ||
1479 std::is_same_v<T, new_token_frame> ||
1480 std::is_same_v<T, handshake_done_frame>)
1481 {
1482 // These frames can be retransmitted
1483 pending_frames_.push_back(frm);
1484 }
1485 else if constexpr (std::is_same_v<T, max_data_frame> ||
1486 std::is_same_v<T, max_stream_data_frame> ||
1487 std::is_same_v<T, max_streams_frame> ||
1488 std::is_same_v<T, data_blocked_frame> ||
1489 std::is_same_v<T, stream_data_blocked_frame> ||
1490 std::is_same_v<T, streams_blocked_frame>)
1491 {
1492 // Flow control frames: queue for retransmission
1493 pending_frames_.push_back(frm);
1494 }
1495 else if constexpr (std::is_same_v<T, reset_stream_frame> ||
1496 std::is_same_v<T, stop_sending_frame>)
1497 {
1498 // Stream control frames: queue for retransmission
1499 pending_frames_.push_back(frm);
1500 }
1501 else if constexpr (std::is_same_v<T, new_connection_id_frame> ||
1502 std::is_same_v<T, retire_connection_id_frame>)
1503 {
1504 // Connection ID management: queue for retransmission
1505 pending_frames_.push_back(frm);
1506 }
1507 else if constexpr (std::is_same_v<T, path_challenge_frame> ||
1508 std::is_same_v<T, path_response_frame>)
1509 {
1510 // Path validation: may need fresh challenge, skip retransmission
1511 (void)frm;
1512 }
1513 else if constexpr (std::is_same_v<T, connection_close_frame>)
1514 {
1515 // CONNECTION_CLOSE is handled specially during closing
1516 (void)frm;
1517 }
1518 },
1519 f);
1520 }
1521}
1522
1524{
1525 sent_packet pkt;
1526 pkt.packet_number = info.packet_number;
1527 pkt.sent_time = info.sent_time;
1528 pkt.sent_bytes = info.sent_bytes;
1529 pkt.ack_eliciting = info.ack_eliciting;
1530 pkt.in_flight = info.in_flight;
1531 pkt.level = info.level;
1532 pkt.frames = info.frames;
1533 return pkt;
1534}
1535
1536// ============================================================================
1537// Peer Connection ID Management
1538// ============================================================================
1539
1541{
1542 // If peer CID manager has CIDs, use the active one
1544 {
1546 }
1547 // Fall back to remote_cid_ for backward compatibility
1548 return remote_cid_;
1549}
1550
1552{
1553 return peer_cid_manager_.rotate_peer_cid();
1554}
1555
1556// ============================================================================
1557// Path MTU Discovery
1558// ============================================================================
1559
1560auto connection::path_mtu() const noexcept -> size_t
1561{
1563}
1564
1566{
1568 // Update congestion controller with current MTU
1570}
1571
1573{
1575 // Reset congestion controller to minimum MTU
1577}
1578
1579auto connection::pmtud_enabled() const noexcept -> bool
1580{
1582}
1583
1584} // namespace kcenon::network::protocols::quic
auto on_packet_acked(const sent_packet &packet, std::chrono::steady_clock::time_point ack_time) -> void
Handle packet acknowledgment (RFC 9002 Section 7.3)
auto set_max_datagram_size(size_t size) -> void
Set max datagram size.
auto on_packet_lost(const sent_packet &packet) -> void
Handle packet loss (RFC 9002 Section 7.3.2)
auto on_ecn_congestion(std::chrono::steady_clock::time_point sent_time) -> void
Handle ECN congestion signal (RFC 9002 Section 7.1)
auto peer_cid_count() const -> size_t
Get the current number of peer CIDs (including retired)
void set_active_cid_limit(uint64_t limit)
Set the active connection ID limit.
auto get_active_peer_cid() const -> const connection_id &
Get the currently active peer connection ID.
void set_initial_peer_cid(const connection_id &cid)
Set the initial peer connection ID (from Initial packet)
QUIC Connection ID (RFC 9000 Section 5.1)
static auto generate(size_t length=8) -> connection_id
Generate a random connection ID.
std::chrono::steady_clock::time_point idle_deadline_
Definition connection.h:593
void generate_probe_packets()
Generate probe packets for PTO (RFC 9002 Section 6.2.4) Sends one or two ack-eliciting packets to pro...
void enable_pmtud()
Enable Path MTU Discovery.
std::deque< std::vector< uint8_t > > pending_crypto_app_
Definition connection.h:575
auto handle_frame(const frame &frame, encryption_level level) -> VoidResult
Handle a specific frame.
void disable_pmtud()
Disable Path MTU Discovery.
std::deque< std::vector< uint8_t > > pending_crypto_initial_
Definition connection.h:573
void update_state()
Update connection state based on handshake progress.
auto next_timeout() const -> std::optional< std::chrono::steady_clock::time_point >
Get the next timeout deadline.
auto is_draining() const noexcept -> bool
Check if connection is draining or closing.
Definition connection.h:203
auto init_server_handshake(const std::string &cert_file, const std::string &key_file) -> VoidResult
Initialize server handshake.
auto rotate_peer_cid() -> VoidResult
Rotate to a new peer connection ID.
auto receive_packet(std::span< const uint8_t > data) -> VoidResult
Receive and process a packet.
void enter_draining()
Transition to draining state.
auto initial_dcid() const -> const connection_id &
Get initial Destination Connection ID (for key derivation)
Definition connection.h:239
void apply_remote_params()
Apply remote transport parameters.
std::chrono::steady_clock::time_point drain_deadline_
Definition connection.h:595
auto get_pn_space(encryption_level level) -> packet_number_space &
Get packet number space for an encryption level.
auto is_server() const noexcept -> bool
Check if this is a server-side connection.
Definition connection.h:220
void set_local_params(const transport_parameters &params)
Set local transport parameters.
auto generate_ack_frame(const packet_number_space &space) -> std::optional< ack_frame >
Generate ACK frame for a packet number space.
auto generate_packets() -> std::vector< std::vector< uint8_t > >
Generate packets to send.
void enter_closing()
Transition to closing state.
auto path_mtu() const noexcept -> size_t
Get current path MTU.
std::optional< uint64_t > close_error_code_
Definition connection.h:588
std::deque< std::vector< uint8_t > > pending_crypto_handshake_
Definition connection.h:574
auto has_pending_data() const -> bool
Check if there are packets to send.
auto retire_cid(uint64_t sequence) -> VoidResult
Retire a Connection ID.
auto is_closed() const noexcept -> bool
Check if connection is closed.
Definition connection.h:212
void queue_frames_for_retransmission(const sent_packet &lost_packet)
Queue frames from lost packet for retransmission.
auto close(uint64_t error_code, const std::string &reason="") -> VoidResult
Close the connection.
auto process_frames(std::span< const uint8_t > payload, encryption_level level) -> VoidResult
Process frames from a decrypted packet.
auto build_packet(encryption_level level) -> std::vector< uint8_t >
Build a packet at the given encryption level.
void handle_loss_detection_result(const loss_detection_result &result)
Handle loss detection result (RFC 9002)
auto start_handshake(const std::string &server_name) -> Result< std::vector< uint8_t > >
Start the handshake (client only)
auto streams() -> stream_manager &
Get the stream manager.
Definition connection.h:371
connection(bool is_server, const connection_id &initial_dcid)
Construct a connection.
auto add_local_cid(const connection_id &cid, uint64_t sequence) -> VoidResult
Add a new local Connection ID.
std::vector< std::pair< uint64_t, connection_id > > local_cids_
Definition connection.h:546
auto process_short_header_packet(const short_header &hdr, std::span< const uint8_t > payload) -> VoidResult
Process a short header packet.
auto close_application(uint64_t error_code, const std::string &reason="") -> VoidResult
Close connection due to application error.
void set_remote_params(const transport_parameters &params)
Set remote transport parameters.
auto process_long_header_packet(const long_header &hdr, std::span< const uint8_t > payload) -> VoidResult
Process a long header packet.
auto active_peer_cid() const -> const connection_id &
Get the currently active peer connection ID.
auto to_sent_packet(const sent_packet_info &info) const -> sent_packet
Convert sent_packet_info to sent_packet for loss detector.
auto pmtud_enabled() const noexcept -> bool
Check if PMTUD is enabled.
void update_send_limit(uint64_t max_data)
Update the send limit (from peer's MAX_DATA)
static auto build_stream(const stream_frame &f, bool include_length=true) -> std::vector< uint8_t >
Build STREAM frame.
Definition frame.cpp:1035
static auto build_ack(const ack_frame &f) -> std::vector< uint8_t >
Build ACK frame.
Definition frame.cpp:950
static auto build_padding(size_t count=1) -> std::vector< uint8_t >
Build PADDING frame.
Definition frame.cpp:940
static auto build_crypto(const crypto_frame &f) -> std::vector< uint8_t >
Build CRYPTO frame.
Definition frame.cpp:1015
static auto build(const frame &f) -> std::vector< uint8_t >
Build any frame from variant.
Definition frame.cpp:891
static auto parse_all(std::span< const uint8_t > data) -> Result< std::vector< frame > >
Parse all frames from buffer.
Definition frame.cpp:343
auto on_timeout() -> loss_detection_result
Handle timeout expiry (RFC 9002 Section 6.2)
auto next_timeout() const -> std::optional< std::chrono::steady_clock::time_point >
Get the time of the next scheduled timeout.
static auto build_handshake(const connection_id &dest_cid, const connection_id &src_cid, uint64_t packet_number, uint32_t version=quic_version::version_1) -> std::vector< uint8_t >
Build a Handshake packet header.
Definition packet.cpp:467
static auto build_short(const connection_id &dest_cid, uint64_t packet_number, bool key_phase=false, bool spin_bit=false) -> std::vector< uint8_t >
Build a Short Header (1-RTT) packet.
Definition packet.cpp:578
static auto build_initial(const connection_id &dest_cid, const connection_id &src_cid, const std::vector< uint8_t > &token, uint64_t packet_number, uint32_t version=quic_version::version_1) -> std::vector< uint8_t >
Build an Initial packet header.
Definition packet.cpp:423
static constexpr auto is_long_header(uint8_t first_byte) noexcept -> bool
Check if a packet has a long header.
Definition packet.h:225
static auto parse_long_header(std::span< const uint8_t > data) -> Result< std::pair< long_header, size_t > >
Parse a long header packet.
Definition packet.cpp:199
static auto parse_short_header(std::span< const uint8_t > data, size_t conn_id_length) -> Result< std::pair< short_header, size_t > >
Parse a short header packet.
Definition packet.cpp:360
static auto protect(const quic_keys &keys, std::span< const uint8_t > header, std::span< const uint8_t > payload, uint64_t packet_number) -> Result< std::vector< uint8_t > >
Protect (encrypt) a QUIC packet.
Definition crypto.cpp:425
auto is_enabled() const noexcept -> bool
Check if PMTUD is enabled.
void enable()
Enable PMTUD and start probing.
auto min_mtu() const noexcept -> size_t
Get minimum MTU.
auto current_mtu() const noexcept -> size_t
Get current validated MTU.
auto get_write_keys(encryption_level level) const -> Result< quic_keys >
Get write keys for an encryption level.
Definition crypto.cpp:1013
auto is_handshake_complete() const noexcept -> bool
Check if the handshake is complete.
Definition crypto.cpp:1003
Manages QUIC streams within a connection.
auto streams_with_pending_data() -> std::vector< stream * >
Get streams with pending data to send.
void set_peer_max_streams_uni(uint64_t max)
Set maximum unidirectional streams peer can initiate.
void set_local_max_streams_uni(uint64_t max)
Set our maximum unidirectional streams (advertised to peer)
void set_local_max_streams_bidi(uint64_t max)
Set our maximum bidirectional streams (advertised to peer)
void set_peer_max_streams_bidi(uint64_t max)
Set maximum bidirectional streams peer can initiate.
static auto create_span(std::string_view name) -> span
Create a new root span with a new trace context.
@ error
Black hole detected, reset to base.
connection_state
QUIC connection state (RFC 9000 Section 5)
Definition connection.h:43
@ connected
Handshake complete, can send/receive data.
@ closing
CONNECTION_CLOSE sent, waiting for timeout.
@ draining
CONNECTION_CLOSE received, draining period.
auto make_default_client_params() -> transport_parameters
Create default client transport parameters.
encryption_level
QUIC encryption levels (RFC 9001 Section 4)
Definition keys.h:54
@ application
1-RTT application data encryption
@ initial
Initial encryption (derived from DCID)
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 handshake_state_to_string(handshake_state state) -> const char *
Convert handshake state to string.
@ ecn_failure
ECN validation failed, should disable ECN.
@ congestion_signal
ECN-CE increased (congestion experienced)
auto make_default_server_params() -> transport_parameters
Create default server transport parameters.
handshake_state
TLS handshake state.
Definition connection.h:56
@ waiting_server_hello
Client waiting for ServerHello.
@ waiting_finished
Waiting for peer's Finished.
auto connection_state_to_string(connection_state state) -> const char *
Convert connection state to string.
auto is_tracing_enabled() -> bool
Check if tracing is enabled.
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
VoidResult ok()
RAII span implementation for distributed tracing.
ACK frame (RFC 9000 Section 19.3)
uint64_t largest_acknowledged
Largest packet number acknowledged.
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.
CRYPTO frame (RFC 9000 Section 19.6)
uint64_t offset
Byte offset in crypto stream.
std::vector< uint8_t > data
Cryptographic handshake data.
HANDSHAKE_DONE frame (RFC 9000 Section 19.20)
QUIC Long Header format (RFC 9000 Section 17.2)
Definition packet.h:95
loss_detection_event event
Event that occurred.
ecn_result ecn_signal
ECN signal from ACK_ECN frame processing.
std::chrono::steady_clock::time_point ecn_congestion_sent_time
Sent time of the packet that triggered ECN congestion signal (used for congestion recovery tracking)
std::vector< sent_packet > acked_packets
Packets that were acknowledged.
std::vector< sent_packet > lost_packets
Packets that were declared lost.
State for each packet number space (Initial, Handshake, Application)
Definition connection.h:118
PING frame (RFC 9000 Section 19.2)
Information about a sent packet for loss detection.
Definition connection.h:97
Information about a sent packet for loss detection (RFC 9002 Section A.1.1)
encryption_level level
Encryption level of the packet.
std::chrono::steady_clock::time_point sent_time
Time the packet was sent.
size_t sent_bytes
Number of bytes in the packet.
bool in_flight
True if the packet is in flight (counted for congestion control)
bool ack_eliciting
True if this packet is ack-eliciting.
std::vector< frame > frames
Frames included in this packet (for retransmission)
QUIC Short Header format (RFC 9000 Section 17.3)
Definition packet.h:138
QUIC transport parameters (RFC 9000 Section 18)
uint64_t max_idle_timeout
Maximum idle timeout in milliseconds (0 = disabled)
uint64_t initial_max_data
Initial maximum data for connection (default: 0)
uint64_t initial_max_streams_uni
Initial maximum unidirectional streams.
uint64_t initial_max_streams_bidi
Initial maximum bidirectional streams.
std::optional< connection_id > initial_source_connection_id
Initial Source Connection ID.
uint64_t active_connection_id_limit
Maximum number of connection IDs from the peer.
Distributed tracing context for OpenTelemetry-compatible tracing.
Configuration structures for OpenTelemetry tracing.