Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
61ea9e5
feat(qpack): implement qpack decoder
0x676e67 Jun 9, 2026
d5efaf8
fix: poll_read data is buffered without bounds
0x676e67 Jun 9, 2026
3eb2c99
fmt
0x676e67 Jun 9, 2026
c67ff9b
fmt
0x676e67 Jun 9, 2026
2fd634a
fmt
0x676e67 Jun 9, 2026
d571c8c
fix dynamic table decompression contention
0x676e67 Jun 10, 2026
e032d2a
update examples
0x676e67 Jun 10, 2026
061fe0c
waker
0x676e67 Jun 10, 2026
b46670b
fix deadlock
0x676e67 Jun 10, 2026
7577f23
channel
0x676e67 Jun 10, 2026
bf8b57a
Merge branch 'main' into qpack-decoder
0x676e67 Jun 12, 2026
4bdf23f
feat(qpack): optimize QPACK dynamic table decoder (#7)
0x676e67 Jun 15, 2026
ae39ca1
fmt
0x676e67 Jun 15, 2026
5ffbf7f
feat(client,qpack): Unblock poll from QPACK dynamic table decoder rea…
0x676e67 Jun 16, 2026
f64cc0a
fmt
0x676e67 Jun 16, 2026
d6a3cd3
refactor(client,qpack): Improve QPACK dynamic table decoder (#9)
0x676e67 Jun 16, 2026
8b097a4
update examples
0x676e67 Jun 16, 2026
5a49ec5
Potential fix for pull request finding
0x676e67 Jun 16, 2026
17dfe6e
Potential fix for pull request finding
0x676e67 Jun 16, 2026
8844f2a
Potential fix for pull request finding
0x676e67 Jun 16, 2026
73af079
fix(client,qpack): Fix wakeup deadlock risk in QPACK decoder rwlock (…
0x676e67 Jun 17, 2026
dc79ad3
perf(client,qpack): Simplify poll_decode_header invocation (#11)
0x676e67 Jun 17, 2026
69c5027
update
0x676e67 Jun 18, 2026
3efbbed
fix
0x676e67 Jun 18, 2026
546aad5
update
0x676e67 Jun 18, 2026
0bd2131
update
0x676e67 Jun 18, 2026
be0418f
fmt
0x676e67 Jun 18, 2026
5510b75
fmt
0x676e67 Jun 18, 2026
2b97d5f
update
0x676e67 Jun 18, 2026
b72f8d2
fmt
0x676e67 Jun 19, 2026
8284279
fmt
0x676e67 Jun 19, 2026
de9fcb1
perf(client, qpack): combine wakers and events into single channel (#13)
0x676e67 Jun 20, 2026
1b8c86f
Remove Debug implementation for SharedState
0x676e67 Jun 20, 2026
45d13a9
Add Debug trait to SharedState struct
0x676e67 Jun 20, 2026
c1e1417
fmt
0x676e67 Jun 20, 2026
344aac6
fix(qpack, client): align QPACK decoder with protocol spec compliance
0x676e67 Jun 20, 2026
0f7548a
docs
0x676e67 Jun 20, 2026
4238459
fix(client,qpack): wait for missing refs without lost wakeups (#12)
0x676e67 Jun 20, 2026
537afe7
Merge branch 'qpack-decoder' into fix-qpack-decoder-compliance
0x676e67 Jun 20, 2026
77230a1
Merge branch 'refactor-http3-rs-layout' into fix-qpack-decoder-compli…
0x676e67 Jun 20, 2026
07bf232
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jun 22, 2026
dea728f
Fix workspace clippy warnings
0x676e67 Jun 22, 2026
ce7404a
Simplify QPACK response header polling
0x676e67 Jun 22, 2026
f7b7db7
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jun 22, 2026
388ac95
update
0x676e67 Jun 23, 2026
01342bc
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jun 26, 2026
4407065
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jun 26, 2026
9103025
Merge remote-tracking branch 'origin/main' into fix-qpack-decoder-com…
0x676e67 Jun 26, 2026
9528541
test(qpack): cover invalid post-base required count
0x676e67 Jun 26, 2026
6b7b495
ci: limit default workspace members
0x676e67 Jul 2, 2026
60968ca
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jul 10, 2026
777d73b
fix(qpack): reject overflowing delta base (#39)
0x676e67 Jul 10, 2026
fb453cc
fix(qpack): accept full blocked streams setting range (#40)
0x676e67 Jul 10, 2026
7e3b7a7
fix(qpack): release blocked streams on decoder updates (#41)
0x676e67 Jul 10, 2026
012079c
test(qpack): accept zero capacity update at zero limit (#42)
0x676e67 Jul 13, 2026
07a1492
fix(qpack): close decoder compliance gaps
0x676e67 Jul 18, 2026
e4dbb82
fmt
0x676e67 Jul 20, 2026
0db6219
docs(qpack): clarify decoder behavior
0x676e67 Jul 23, 2026
8bb72d8
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jul 28, 2026
d44071d
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jul 28, 2026
fad4f4d
fmt
0x676e67 Jul 28, 2026
6956f2f
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jul 28, 2026
f03fe1a
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Jul 29, 2026
91a78b0
fix(qpack): reject invalid negative delta base (#52)
0x676e67 Aug 10, 2026
20243b2
fix(qpack): publish errors before waking waiters (#53)
0x676e67 Aug 10, 2026
894b034
perf(qpack): avoid retaining decoder access wakers (#54)
0x676e67 Aug 13, 2026
e3d3e89
perf(buf): cache remaining byte count (#55)
0x676e67 Aug 14, 2026
389daac
perf(qpack): avoid decoder lookup map maintenance (#57)
0x676e67 Aug 23, 2026
4fe354b
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Aug 23, 2026
32f6384
Merge remote-tracking branch 'origin/fix-qpack-decoder-compliance' in…
0x676e67 Aug 23, 2026
72bbd7d
perf(qpack): preserve fragmented literal decode progress (#56)
0x676e67 Aug 23, 2026
3f7ea3c
Merge remote-tracking branch 'origin/fix-qpack-decoder-compliance' in…
0x676e67 Aug 23, 2026
a1959a6
fix(qpack): validate insert count increments (#62)
0x676e67 Aug 25, 2026
944ecbd
Merge branch 'main' into fix-qpack-decoder-compliance
0x676e67 Aug 25, 2026
8f8cb7d
fix(qpack): drive peer decoder stream (#63)
0x676e67 Aug 27, 2026
613ed42
fix(server): drive peer QPACK encoder stream (#64)
0x676e67 Aug 27, 2026
0d8c9dd
fix(qpack): clarify zero required insert count semantics (#65)
0x676e67 Aug 27, 2026
cbd72e2
Merge remote-tracking branch 'origin/main' into fix-qpack-decoder-com…
0x676e67 Aug 27, 2026
a50bde1
fix(qpack): publish errors before waking waiters
0x676e67 Aug 27, 2026
8ebd596
fix(qpack): track decoder feedback per field section
0x676e67 Aug 27, 2026
c01b8e6
Merge pull request #66 from 0x676e67/fix-qpack-error-publish-order
0x676e67 Aug 27, 2026
ef8d568
Merge pull request #67 from 0x676e67/fix-qpack-decoder-feedback-state
0x676e67 Aug 27, 2026
d28584f
fix(qpack): harden decoder driver error handling
0x676e67 Aug 27, 2026
5badecc
ci: use nightly rustfmt
0x676e67 Aug 27, 2026
8c603a8
Merge pull request #68 from 0x676e67/fix-qpack-final-driver-audit
0x676e67 Aug 28, 2026
c0f009b
update Cargo.lock
0x676e67 Aug 28, 2026
9598a3d
fix(qpack): harden decoder protocol boundaries
0x676e67 Aug 28, 2026
dc0401c
fix(qpack): gate stateless decoder export
0x676e67 Aug 28, 2026
a28f429
Merge pull request #69 from 0x676e67/fix-qpack-p1-protocol-boundaries
0x676e67 Aug 28, 2026
e1120a3
fix(qpack): preserve decoder state metadata (#70)
0x676e67 Aug 28, 2026
e064748
docs(qpack): correct protocol terminology (#71)
0x676e67 Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ env:
core_package_args: >-
--workspace
--exclude interop
toolchain_style: stable
toolchain_style: nightly
toolchain_msrv: "1.85"
toolchain_http3_quinn_msrv: "1.85"
toolchain_doc: nightly
Expand Down Expand Up @@ -45,7 +45,7 @@ jobs:
with:
toolchain: ${{ env.toolchain_style }}
components: rustfmt
- run: cargo fmt --all -- --check
- run: cargo +${{ env.toolchain_style }} fmt --all --check

lint:
name: Lint
Expand Down
131 changes: 125 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use std::{path::PathBuf, sync::Arc};
use bytes::{Buf, Bytes};
use futures::future;
use http3::error::{Code, ConnectionError, StreamError};
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::crypto::WebPkiSupportedAlgorithms;
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::{DigitallySignedStruct, SignatureScheme};
use rustls::{
DigitallySignedStruct, SignatureScheme,
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
crypto::WebPkiSupportedAlgorithms,
pki_types::{CertificateDer, ServerName, UnixTime},
};
use rustls_native_certs::CertificateResult;
use structopt::StructOpt;
use tracing::{Level, error, info};
Expand Down
3 changes: 2 additions & 1 deletion http3-datagram/src/datagram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ where

#[cfg(test)]
mod tests {
use super::Datagram;
use bytes::{Buf, Bytes};
use http3::quic::StreamId;

use super::Datagram;

#[test]
fn encode_preserves_quarter_stream_id() {
let stream_id = StreamId::try_from(4).unwrap();
Expand Down
6 changes: 2 additions & 4 deletions http3-quic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,11 @@ compile_error!("features `quinn` and `quic` are mutually exclusive");

#[cfg(all(feature = "quic", not(feature = "quinn")))]
pub use quic;
#[cfg(all(feature = "quinn", not(feature = "quic")))]
pub use quinn as quic;

use quic::ReadError;
#[cfg(any(feature = "quinn", feature = "quic"))]
pub use quic::{AcceptBi, AcceptUni, Endpoint, OpenBi, OpenUni, VarInt};

#[cfg(all(feature = "quinn", not(feature = "quic")))]
pub use quinn as quic;
use tokio_util::sync::ReusableBoxFuture;
#[cfg(feature = "tracing")]
use tracing::instrument;
Expand Down
37 changes: 33 additions & 4 deletions http3/src/buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@ use bytes::{Buf, Bytes};
#[derive(Debug)]
pub(crate) struct BufList<T> {
bufs: VecDeque<T>,
remaining: usize,
}

impl<T: Buf> BufList<T> {
pub(crate) fn new() -> BufList<T> {
BufList {
bufs: VecDeque::new(),
remaining: 0,
}
}

#[inline]
#[allow(dead_code)]
pub(crate) fn push(&mut self, buf: T) {
debug_assert!(buf.has_remaining());
self.remaining = self.remaining.saturating_add(buf.remaining());
self.bufs.push_back(buf);
}

Expand All @@ -33,7 +35,9 @@ impl<T: Buf> BufList<T> {

impl BufList<Bytes> {
pub fn take_first_chunk(&mut self) -> Option<Bytes> {
self.bufs.pop_front()
let chunk = self.bufs.pop_front()?;
self.remaining = self.remaining.saturating_sub(chunk.remaining());
Some(chunk)
}

pub fn take_chunk(&mut self, max_len: usize) -> Option<Bytes> {
Expand All @@ -42,6 +46,10 @@ impl BufList<Bytes> {
.front_mut()
.map(|chunk| chunk.split_to(usize::min(max_len, chunk.remaining())));

if let Some(chunk) = chunk.as_ref() {
self.remaining = self.remaining.saturating_sub(chunk.remaining());
}

if let Some(front) = self.bufs.front() {
if front.remaining() == 0 {
let _ = self.bufs.pop_front();
Expand All @@ -55,7 +63,7 @@ impl BufList<Bytes> {
T: Buf,
{
debug_assert!(buf.has_remaining());
self.bufs.push_back(buf.copy_to_bytes(buf.remaining()))
self.push(buf.copy_to_bytes(buf.remaining()))
}
}

Expand All @@ -71,7 +79,7 @@ impl<T: Buf> From<T> for BufList<T> {
impl<T: Buf> Buf for BufList<T> {
#[inline]
fn remaining(&self) -> usize {
self.bufs.iter().map(|buf| buf.remaining()).sum()
self.remaining
}

#[inline]
Expand All @@ -81,6 +89,9 @@ impl<T: Buf> Buf for BufList<T> {

#[inline]
fn advance(&mut self, mut cnt: usize) {
assert!(cnt <= self.remaining, "cannot advance past the buffer");
self.remaining -= cnt;

while cnt > 0 {
{
let front = &mut self.bufs[0];
Expand Down Expand Up @@ -113,6 +124,7 @@ impl<T: Buf> Buf for BufList<T> {
}
}

#[derive(Clone)]
pub struct Cursor<'a, B> {
buf: &'a BufList<B>,
pos_total: usize, // position amongst all bytes
Expand Down Expand Up @@ -179,4 +191,21 @@ mod tests {
cur.advance(2);
assert_eq!(cur.remaining(), 0);
}

#[test]
fn remaining_tracks_buffer_mutations() {
let mut buf = BufList::new();
buf.push(Bytes::from_static(b"abc"));
buf.push(Bytes::from_static(b"de"));
assert_eq!(buf.remaining(), 5);

assert_eq!(buf.take_chunk(2), Some(Bytes::from_static(b"ab")));
assert_eq!(buf.remaining(), 3);

buf.advance(2);
assert_eq!(buf.remaining(), 1);

assert_eq!(buf.take_first_chunk(), Some(Bytes::from_static(b"e")));
assert_eq!(buf.remaining(), 0);
}
}
Loading
Loading