Commit 5c9816ab authored by jan.koester's avatar jan.koester
Browse files

test

parent bc8cdbd2
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -7327,6 +7327,12 @@ size_t quic::sendStreamDataEarly(uint64_t stream_id, const uint8_t* data, size_t
    if (stream.send_offset + send_len > stream.max_data_remote) {
        send_len = (stream.max_data_remote > stream.send_offset) ? (stream.max_data_remote - stream.send_offset) : 0;
    }
    // FIN declares the stream's final size.  If flow control only admits a
    // prefix of the caller's buffer, that prefix is not the final write: the
    // caller will retry the remainder after receiving more credit.  Attaching
    // FIN here would declare a too-small final size and make that retry a
    // FINAL_SIZE_ERROR at the peer.
    const bool fin_eligible = fin && send_len == len;
    if (send_len == 0 && !fin) return 0;

    // Conservative fixed chunk size — 0-RTT packets are padded to the same
@@ -7340,7 +7346,7 @@ size_t quic::sendStreamDataEarly(uint64_t stream_id, const uint8_t* data, size_t
    do {
        size_t chunk = std::min(send_len - sent_total, max_chunk);
        bool is_last = (sent_total + chunk >= send_len);
        bool set_fin = fin && is_last;
        bool set_fin = fin_eligible && is_last;

        std::vector<uint8_t> chunk_data(data + sent_total, data + sent_total + chunk);
        std::vector<uint8_t> stream_frame = buildStreamFrame(stream_id, chunk_data, stream.send_offset, set_fin);
@@ -7441,6 +7447,11 @@ size_t quic::sendStreamData(uint64_t stream_id, const uint8_t* data, size_t len,
                   (stream.max_data_remote - stream.send_offset) : 0;
    }

    // A STREAM FIN fixes the final stream size permanently.  Completion of a
    // flow-control-clamped prefix is therefore not completion of the caller's
    // write.  Only the call that can accept all `len` bytes may carry FIN.
    const bool fin_eligible = fin && send_len == len;
    
    if (send_len == 0 && !fin) {
        return 0;
    }
@@ -7506,7 +7517,7 @@ size_t quic::sendStreamData(uint64_t stream_id, const uint8_t* data, size_t len,
    while (sent_total < send_len) {
        size_t chunk = std::min(send_len - sent_total, max_chunk);
        bool is_last = (sent_total + chunk >= send_len);
        bool set_fin = fin && is_last;
        bool set_fin = fin_eligible && is_last;

        // Congestion control: use estimated actual packet size, not max_datagram
        size_t est_pkt_size = header_overhead + frame_hdr_max + chunk;
@@ -7731,7 +7742,7 @@ size_t quic::sendStreamData(uint64_t stream_id, const uint8_t* data, size_t len,

    // Only mark FIN if we actually sent all requested data (FIN is on the last chunk).
    // If CC broke the loop early, the FIN was never put on the wire.
    if (fin && sent_total >= send_len) {
    if (fin_eligible && sent_total >= send_len) {
        // FIN-only: when send_len == 0, the main loop never ran, so we must
        // send an empty STREAM frame with the FIN bit on the wire.
        if (send_len == 0 && !stream.send_fin) {
+44 −0
Original line number Diff line number Diff line
@@ -497,6 +497,49 @@ static void test_empty_fin() {
    }
}

// ============================================================================
// RFC 9000 §4.5: Flow-control truncation must not declare a final size
// ============================================================================

static void test_flow_control_truncated_fin() {
    std::cout << "\n=== RFC 9000 §4.5: FIN after flow-control truncation ===" << std::endl;

    quic client;
    client.setTrustPolicy(netplus::TlsTrustPolicy{false});
    connectClient(client);
    uint64_t sid = client.openStream(true);

    std::vector<uint8_t> payload(512);
    fillPattern(payload, 0x5A);

    // 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 prefix = 128;
    client._max_data_remote = client._data_sent + 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,
          "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;
    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,
          "Final write sets the stream FIN state");

    std::vector<uint8_t> recv_buf(payload.size() + 4096);
    size_t total = drainStream(client, sid, recv_buf, 5);
    check(total == payload.size(), "Peer receives the complete final-size payload");
    check(total == payload.size() &&
              std::memcmp(payload.data(), recv_buf.data(), payload.size()) == 0,
          "Payload remains intact across the flow-control boundary");
}

// ============================================================================
// RFC 9000 §4.6: MAX_STREAMS — Rapid stream creation
// ============================================================================
@@ -680,6 +723,7 @@ int main() {
    test_uni_streams();
    test_fin_handling();
    test_empty_fin();
    test_flow_control_truncated_fin();
    test_data_integrity_small();
    test_data_integrity_large();
    test_connection_flow_control();