Loading src/socket.h +1 −0 Original line number Diff line number Diff line Loading @@ -2070,6 +2070,7 @@ namespace netplus { friend class event; friend class EventWorker; friend class EventWorkerArgs; friend struct QuicRfc9000TestAccess; }; bool isIPAddr(const std::string &host); Loading test/quic_rfc9000_test.cpp +58 −15 Original line number Diff line number Diff line Loading @@ -15,19 +15,60 @@ #include <numeric> #include <algorithm> // White-box access is limited to this test translation unit. Deterministically // exercising a flow-control boundary requires lowering the peer-credit state; // there is intentionally no production API for mutating that protocol state. #define private public #include "connection.h" #include "eventapi.h" #include "socket.h" #include "exception.h" #undef private #include "https_certs.h" #include "https_ca_cert.h" // White-box access is limited to this test accessor. Deterministically // exercising flow-control and PTO boundaries requires mutating protocol state; // there is intentionally no production API for doing that. namespace netplus { struct QuicRfc9000TestAccess { static uint64_t maxDataRemote(const quic& endpoint) { return endpoint._max_data_remote; } static void setMaxDataRemote(quic& endpoint, uint64_t value) { endpoint._max_data_remote = value; } static uint64_t dataSent(const quic& endpoint) { return endpoint._data_sent; } static bool streamSendFin(const quic& endpoint, uint64_t stream_id) { const auto it = endpoint._streams.find(stream_id); return it != endpoint._streams.end() && it->second.send_fin; } static void recordSentPacket(quic& endpoint, uint64_t pn, uint64_t stream_id, uint64_t stream_offset, const uint8_t* data, size_t len, bool fin, size_t wire_size) { endpoint.recordSentPacket(pn, stream_id, stream_offset, data, len, fin, wire_size); } static void setLargestAckedPacketNumber(quic& endpoint, uint64_t value) { endpoint._largest_acked_pn = value; } static void setPtoCount(quic& endpoint, int value) { endpoint._pto_count = value; } static int ptoCount(const quic& endpoint) { return endpoint._pto_count; } static void checkLossAndRetransmit(quic& endpoint) { endpoint.checkLossAndRetransmit(); } }; } // namespace netplus using namespace netplus; static std::atomic<bool> g_server_ready(false); Loading Loading @@ -520,22 +561,23 @@ static void test_flow_control_truncated_fin() { // Artificially restrict the sender's view of the peer's connection // credit so the pointer overload accepts only a prefix of this write. const uint64_t original_max_data = client._max_data_remote; const uint64_t original_max_data = QuicRfc9000TestAccess::maxDataRemote(client); const uint64_t prefix = 128; client._max_data_remote = client._data_sent + prefix; QuicRfc9000TestAccess::setMaxDataRemote( client, QuicRfc9000TestAccess::dataSent(client) + prefix); size_t first = client.sendStreamData(sid, payload.data(), payload.size(), true); check(first == prefix, "Flow control accepts only the available prefix"); check(!client._streams[sid].send_fin, check(!QuicRfc9000TestAccess::streamSendFin(client, sid), "Truncated prefix does not set the stream FIN state"); // Restore credit and send the remainder. This call, and only this call, // may declare the final size. client._max_data_remote = original_max_data; QuicRfc9000TestAccess::setMaxDataRemote(client, original_max_data); size_t second = client.sendStreamData(sid, payload.data() + first, payload.size() - first, true); check(first + second == payload.size(), "Remainder is accepted after credit returns"); check(client._streams[sid].send_fin, check(QuicRfc9000TestAccess::streamSendFin(client, sid), "Final write sets the stream FIN state"); std::vector<uint8_t> recv_buf(payload.size() + 4096); Loading @@ -555,12 +597,13 @@ static void test_pto_backoff_persists_without_new_ack() { quic endpoint; uint8_t payload = 0x42; endpoint.recordSentPacket(100, 0, 0, &payload, 1, false, 1); endpoint._largest_acked_pn = 0; // An ACK existed before this loss scan. endpoint._pto_count = 3; QuicRfc9000TestAccess::recordSentPacket(endpoint, 100, 0, 0, &payload, 1, false, 1); QuicRfc9000TestAccess::setLargestAckedPacketNumber( endpoint, 0); // An ACK existed before this loss scan. QuicRfc9000TestAccess::setPtoCount(endpoint, 3); endpoint.checkLossAndRetransmit(); check(endpoint._pto_count == 3, QuicRfc9000TestAccess::checkLossAndRetransmit(endpoint); check(QuicRfc9000TestAccess::ptoCount(endpoint) == 3, "Loss scan without a new ACK preserves PTO exponential backoff"); } Loading Loading
src/socket.h +1 −0 Original line number Diff line number Diff line Loading @@ -2070,6 +2070,7 @@ namespace netplus { friend class event; friend class EventWorker; friend class EventWorkerArgs; friend struct QuicRfc9000TestAccess; }; bool isIPAddr(const std::string &host); Loading
test/quic_rfc9000_test.cpp +58 −15 Original line number Diff line number Diff line Loading @@ -15,19 +15,60 @@ #include <numeric> #include <algorithm> // White-box access is limited to this test translation unit. Deterministically // exercising a flow-control boundary requires lowering the peer-credit state; // there is intentionally no production API for mutating that protocol state. #define private public #include "connection.h" #include "eventapi.h" #include "socket.h" #include "exception.h" #undef private #include "https_certs.h" #include "https_ca_cert.h" // White-box access is limited to this test accessor. Deterministically // exercising flow-control and PTO boundaries requires mutating protocol state; // there is intentionally no production API for doing that. namespace netplus { struct QuicRfc9000TestAccess { static uint64_t maxDataRemote(const quic& endpoint) { return endpoint._max_data_remote; } static void setMaxDataRemote(quic& endpoint, uint64_t value) { endpoint._max_data_remote = value; } static uint64_t dataSent(const quic& endpoint) { return endpoint._data_sent; } static bool streamSendFin(const quic& endpoint, uint64_t stream_id) { const auto it = endpoint._streams.find(stream_id); return it != endpoint._streams.end() && it->second.send_fin; } static void recordSentPacket(quic& endpoint, uint64_t pn, uint64_t stream_id, uint64_t stream_offset, const uint8_t* data, size_t len, bool fin, size_t wire_size) { endpoint.recordSentPacket(pn, stream_id, stream_offset, data, len, fin, wire_size); } static void setLargestAckedPacketNumber(quic& endpoint, uint64_t value) { endpoint._largest_acked_pn = value; } static void setPtoCount(quic& endpoint, int value) { endpoint._pto_count = value; } static int ptoCount(const quic& endpoint) { return endpoint._pto_count; } static void checkLossAndRetransmit(quic& endpoint) { endpoint.checkLossAndRetransmit(); } }; } // namespace netplus using namespace netplus; static std::atomic<bool> g_server_ready(false); Loading Loading @@ -520,22 +561,23 @@ static void test_flow_control_truncated_fin() { // Artificially restrict the sender's view of the peer's connection // credit so the pointer overload accepts only a prefix of this write. const uint64_t original_max_data = client._max_data_remote; const uint64_t original_max_data = QuicRfc9000TestAccess::maxDataRemote(client); const uint64_t prefix = 128; client._max_data_remote = client._data_sent + prefix; QuicRfc9000TestAccess::setMaxDataRemote( client, QuicRfc9000TestAccess::dataSent(client) + prefix); size_t first = client.sendStreamData(sid, payload.data(), payload.size(), true); check(first == prefix, "Flow control accepts only the available prefix"); check(!client._streams[sid].send_fin, check(!QuicRfc9000TestAccess::streamSendFin(client, sid), "Truncated prefix does not set the stream FIN state"); // Restore credit and send the remainder. This call, and only this call, // may declare the final size. client._max_data_remote = original_max_data; QuicRfc9000TestAccess::setMaxDataRemote(client, original_max_data); size_t second = client.sendStreamData(sid, payload.data() + first, payload.size() - first, true); check(first + second == payload.size(), "Remainder is accepted after credit returns"); check(client._streams[sid].send_fin, check(QuicRfc9000TestAccess::streamSendFin(client, sid), "Final write sets the stream FIN state"); std::vector<uint8_t> recv_buf(payload.size() + 4096); Loading @@ -555,12 +597,13 @@ static void test_pto_backoff_persists_without_new_ack() { quic endpoint; uint8_t payload = 0x42; endpoint.recordSentPacket(100, 0, 0, &payload, 1, false, 1); endpoint._largest_acked_pn = 0; // An ACK existed before this loss scan. endpoint._pto_count = 3; QuicRfc9000TestAccess::recordSentPacket(endpoint, 100, 0, 0, &payload, 1, false, 1); QuicRfc9000TestAccess::setLargestAckedPacketNumber( endpoint, 0); // An ACK existed before this loss scan. QuicRfc9000TestAccess::setPtoCount(endpoint, 3); endpoint.checkLossAndRetransmit(); check(endpoint._pto_count == 3, QuicRfc9000TestAccess::checkLossAndRetransmit(endpoint); check(QuicRfc9000TestAccess::ptoCount(endpoint) == 3, "Loss scan without a new ACK preserves PTO exponential backoff"); } Loading