From 0eb237f7973e232c73f58a4ebc7227c27070cbaa Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sun, 29 Jun 2025 11:42:02 +0800 Subject: [PATCH] add a Config and ConnectionState flag for RESET_STREAM_AT (#5243) * add a Config and ConnectionState flag for RESET_STREAM_AT * add RESET_STREAM_AT to README --- README.md | 1 + config.go | 37 +++++++++++++++++++------------------ config_test.go | 6 ++++-- connection.go | 8 +++++++- interface.go | 7 ++++++- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ccc9e2133..246c33049 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ In addition to these base RFCs, it also implements the following RFCs: * Datagram Packetization Layer Path MTU Discovery (DPLPMTUD, [RFC 8899](https://datatracker.ietf.org/doc/html/rfc8899)) * QUIC Version 2 ([RFC 9369](https://datatracker.ietf.org/doc/html/rfc9369)) * QUIC Event Logging using qlog ([draft-ietf-quic-qlog-main-schema](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-main-schema/) and [draft-ietf-quic-qlog-quic-events](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-quic-events/)) +* QUIC Stream Resets with Partial Delivery ([draft-ietf-quic-reliable-stream-reset](https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset-07)) Support for WebTransport over HTTP/3 ([draft-ietf-webtrans-http3](https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/)) is implemented in [webtransport-go](https://github.com/quic-go/webtransport-go). diff --git a/config.go b/config.go index 540a3240b..74c2054e4 100644 --- a/config.go +++ b/config.go @@ -106,23 +106,24 @@ func populateConfig(config *Config) *Config { } return &Config{ - GetConfigForClient: config.GetConfigForClient, - Versions: versions, - HandshakeIdleTimeout: handshakeIdleTimeout, - MaxIdleTimeout: idleTimeout, - KeepAlivePeriod: config.KeepAlivePeriod, - InitialStreamReceiveWindow: initialStreamReceiveWindow, - MaxStreamReceiveWindow: maxStreamReceiveWindow, - InitialConnectionReceiveWindow: initialConnectionReceiveWindow, - MaxConnectionReceiveWindow: maxConnectionReceiveWindow, - AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, - MaxIncomingStreams: maxIncomingStreams, - MaxIncomingUniStreams: maxIncomingUniStreams, - TokenStore: config.TokenStore, - EnableDatagrams: config.EnableDatagrams, - InitialPacketSize: initialPacketSize, - DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, - Allow0RTT: config.Allow0RTT, - Tracer: config.Tracer, + GetConfigForClient: config.GetConfigForClient, + Versions: versions, + HandshakeIdleTimeout: handshakeIdleTimeout, + MaxIdleTimeout: idleTimeout, + KeepAlivePeriod: config.KeepAlivePeriod, + InitialStreamReceiveWindow: initialStreamReceiveWindow, + MaxStreamReceiveWindow: maxStreamReceiveWindow, + InitialConnectionReceiveWindow: initialConnectionReceiveWindow, + MaxConnectionReceiveWindow: maxConnectionReceiveWindow, + AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, + MaxIncomingStreams: maxIncomingStreams, + MaxIncomingUniStreams: maxIncomingUniStreams, + TokenStore: config.TokenStore, + EnableDatagrams: config.EnableDatagrams, + InitialPacketSize: initialPacketSize, + DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, + EnableStreamResetPartialDelivery: config.EnableStreamResetPartialDelivery, + Allow0RTT: config.Allow0RTT, + Tracer: config.Tracer, } } diff --git a/config_test.go b/config_test.go index b1c1c6c6e..b8e4fc391 100644 --- a/config_test.go +++ b/config_test.go @@ -126,6 +126,8 @@ func configWithNonZeroNonFunctionFields(t *testing.T) *Config { f.Set(reflect.ValueOf(true)) case "Allow0RTT": f.Set(reflect.ValueOf(true)) + case "EnableStreamResetPartialDelivery": + f.Set(reflect.ValueOf(true)) default: t.Fatalf("all fields must be accounted for, but saw unknown field %q", fn) } @@ -133,7 +135,7 @@ func configWithNonZeroNonFunctionFields(t *testing.T) *Config { return c } -func TestConfigCloning(t *testing.T) { +func TestConfigClone(t *testing.T) { t.Run("function fields", func(t *testing.T) { var calledAllowConnectionWindowIncrease, calledTracer bool c1 := &Config{ @@ -153,7 +155,7 @@ func TestConfigCloning(t *testing.T) { require.True(t, calledTracer) }) - t.Run("clones non-function fields", func(t *testing.T) { + t.Run("non-function fields", func(t *testing.T) { c := configWithNonZeroNonFunctionFields(t) require.Equal(t, c, c.Clone()) }) diff --git a/connection.go b/connection.go index a6af5fcda..ce565aa01 100644 --- a/connection.go +++ b/connection.go @@ -315,6 +315,7 @@ var newConnection = func( ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, InitialSourceConnectionID: srcConnID, RetrySourceConnectionID: retrySrcConnID, + EnableResetStreamAt: conf.EnableStreamResetPartialDelivery, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = wire.MaxDatagramSize @@ -425,6 +426,7 @@ var newClientConnection = func( // See https://github.com/quic-go/quic-go/pull/3806. ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, InitialSourceConnectionID: srcConnID, + EnableResetStreamAt: conf.EnableStreamResetPartialDelivery, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = wire.MaxDatagramSize @@ -468,7 +470,10 @@ func (c *Conn) preSetup() { c.handshakeStream = newCryptoStream() c.sendQueue = newSendQueue(c.conn) c.retransmissionQueue = newRetransmissionQueue() - c.frameParser = *wire.NewFrameParser(c.config.EnableDatagrams, false) + c.frameParser = *wire.NewFrameParser( + c.config.EnableDatagrams, + c.config.EnableStreamResetPartialDelivery, + ) c.rttStats = &utils.RTTStats{} c.connFlowController = flowcontrol.NewConnectionFlowController( protocol.ByteCount(c.config.InitialConnectionReceiveWindow), @@ -722,6 +727,7 @@ func (c *Conn) ConnectionState() ConnectionState { cs := c.cryptoStreamHandler.ConnectionState() c.connState.TLS = cs.ConnectionState c.connState.Used0RTT = cs.Used0RTT + c.connState.SupportsStreamResetPartialDelivery = c.peerParams.EnableResetStreamAt c.connState.GSO = c.conn.capabilities().GSO return c.connState } diff --git a/interface.go b/interface.go index 4ba75a378..70dcdef1c 100644 --- a/interface.go +++ b/interface.go @@ -179,7 +179,10 @@ type Config struct { Allow0RTT bool // Enable QUIC datagram support (RFC 9221). EnableDatagrams bool - Tracer func(context.Context, logging.Perspective, ConnectionID) *logging.ConnectionTracer + // Enable QUIC Stream Resets with Partial Delivery. + // See https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset-07. + EnableStreamResetPartialDelivery bool + Tracer func(context.Context, logging.Perspective, ConnectionID) *logging.ConnectionTracer } // ClientHelloInfo contains information about an incoming connection attempt. @@ -207,6 +210,8 @@ type ConnectionState struct { // This is a unilateral declaration by the peer - receiving datagrams is only possible if // datagram support was enabled locally via Config.EnableDatagrams. SupportsDatagrams bool + // SupportsStreamResetPartialDelivery indicates whether the peer advertised support for QUIC Stream Resets with Partial Delivery. + SupportsStreamResetPartialDelivery bool // Used0RTT says if 0-RTT resumption was used. Used0RTT bool // Version is the QUIC version of the QUIC connection.