Loading src/quic.cpp +46 −0 Original line number Diff line number Diff line Loading @@ -2880,6 +2880,16 @@ bool quic::isStreamComplete(uint64_t stream_id) const { return it->second.recv_complete; } bool quic::isStreamReset(uint64_t stream_id) const { std::lock_guard<std::recursive_mutex> lock(quic_mtx()); auto it = _streams.find(stream_id); if (it == _streams.end()) { return false; } return it->second.reset; } // ============================================================================ // Frame Processing: Process individual frames // ============================================================================ Loading Loading @@ -5435,6 +5445,42 @@ void quic::closeStream(uint64_t stream_id) { } } // ============================================================================ // RESET_STREAM: Abort a stream abnormally (RFC 9000 §19.4) // ============================================================================ void quic::resetStream(uint64_t stream_id, uint64_t error_code) { std::lock_guard<std::recursive_mutex> lock(quic_mtx()); auto it = _streams.find(stream_id); uint64_t final_size = (it != _streams.end()) ? it->second.send_offset : 0; std::vector<uint8_t> frame; frame.push_back(0x04); // RESET_STREAM frame type uint8_t buf[8]; size_t bytes = encodeVarInt(stream_id, buf); frame.insert(frame.end(), buf, buf + bytes); bytes = encodeVarInt(error_code, buf); frame.insert(frame.end(), buf, buf + bytes); bytes = encodeVarInt(final_size, buf); frame.insert(frame.end(), buf, buf + bytes); std::vector<uint8_t> packet = buildShortHeaderPacket(frame); const uint8_t* hp_key = _is_server ? _app_hp_server : _app_hp_client; applyHeaderProtection(packet, hp_key); ssize_t sent = sendPacket(packet.data(), packet.size()); if (sent < 0) { // Best-effort immediate retry — do not sleep here since the // caller typically holds quic_mtx(). sendPacket(packet.data(), packet.size()); } // No further STREAM frames should follow an abort. if (it != _streams.end()) { it->second.send_fin = true; } } // ============================================================================ // TLS 1.3 Handshake: sendClientHello (wrapper for connect flow) // ============================================================================ Loading src/socket.h +7 −0 Original line number Diff line number Diff line Loading @@ -642,6 +642,13 @@ namespace netplus { size_t recvStreamData(uint64_t stream_id, uint8_t* data, size_t max_len); bool hasStreamData(uint64_t stream_id) const; bool isStreamComplete(uint64_t stream_id) const; bool isStreamReset(uint64_t stream_id) const; // Abort a stream abnormally (RFC 9000 §19.4 RESET_STREAM). Use this // instead of sendStreamData(..., fin=true) when the body falls short // of the advertised Content-Length, so the peer sees a failed // transfer instead of silently accepting a truncated body as complete. void resetStream(uint64_t stream_id, uint64_t error_code = 0x0102); // Read raw UDP datagrams and process QUIC packets without consuming // stream data. Use this in HTTP/3 client loops to pump the network Loading Loading
src/quic.cpp +46 −0 Original line number Diff line number Diff line Loading @@ -2880,6 +2880,16 @@ bool quic::isStreamComplete(uint64_t stream_id) const { return it->second.recv_complete; } bool quic::isStreamReset(uint64_t stream_id) const { std::lock_guard<std::recursive_mutex> lock(quic_mtx()); auto it = _streams.find(stream_id); if (it == _streams.end()) { return false; } return it->second.reset; } // ============================================================================ // Frame Processing: Process individual frames // ============================================================================ Loading Loading @@ -5435,6 +5445,42 @@ void quic::closeStream(uint64_t stream_id) { } } // ============================================================================ // RESET_STREAM: Abort a stream abnormally (RFC 9000 §19.4) // ============================================================================ void quic::resetStream(uint64_t stream_id, uint64_t error_code) { std::lock_guard<std::recursive_mutex> lock(quic_mtx()); auto it = _streams.find(stream_id); uint64_t final_size = (it != _streams.end()) ? it->second.send_offset : 0; std::vector<uint8_t> frame; frame.push_back(0x04); // RESET_STREAM frame type uint8_t buf[8]; size_t bytes = encodeVarInt(stream_id, buf); frame.insert(frame.end(), buf, buf + bytes); bytes = encodeVarInt(error_code, buf); frame.insert(frame.end(), buf, buf + bytes); bytes = encodeVarInt(final_size, buf); frame.insert(frame.end(), buf, buf + bytes); std::vector<uint8_t> packet = buildShortHeaderPacket(frame); const uint8_t* hp_key = _is_server ? _app_hp_server : _app_hp_client; applyHeaderProtection(packet, hp_key); ssize_t sent = sendPacket(packet.data(), packet.size()); if (sent < 0) { // Best-effort immediate retry — do not sleep here since the // caller typically holds quic_mtx(). sendPacket(packet.data(), packet.size()); } // No further STREAM frames should follow an abort. if (it != _streams.end()) { it->second.send_fin = true; } } // ============================================================================ // TLS 1.3 Handshake: sendClientHello (wrapper for connect flow) // ============================================================================ Loading
src/socket.h +7 −0 Original line number Diff line number Diff line Loading @@ -642,6 +642,13 @@ namespace netplus { size_t recvStreamData(uint64_t stream_id, uint8_t* data, size_t max_len); bool hasStreamData(uint64_t stream_id) const; bool isStreamComplete(uint64_t stream_id) const; bool isStreamReset(uint64_t stream_id) const; // Abort a stream abnormally (RFC 9000 §19.4 RESET_STREAM). Use this // instead of sendStreamData(..., fin=true) when the body falls short // of the advertised Content-Length, so the peer sees a failed // transfer instead of silently accepting a truncated body as complete. void resetStream(uint64_t stream_id, uint64_t error_code = 0x0102); // Read raw UDP datagrams and process QUIC packets without consuming // stream data. Use this in HTTP/3 client loops to pump the network Loading