wire: implement parsing and writing of the IMMEDIATE_ACK frame (#5265)

This commit is contained in:
Marten Seemann
2025-07-21 09:56:04 -04:00
committed by GitHub
parent 06e31852cb
commit b69abbaf11
6 changed files with 81 additions and 4 deletions

View File

@@ -43,6 +43,7 @@ func TestIsFrameTypeAckEliciting(t *testing.T) {
wire.FrameTypeDatagramNoLength: true,
wire.FrameTypeDatagramWithLength: true,
wire.FrameTypeAckFrequency: true,
wire.FrameTypeImmediateAck: true,
}
for ft, expected := range testCases {
@@ -63,6 +64,7 @@ func TestAckElicitingFrames(t *testing.T) {
&wire.MaxStreamDataFrame{}: true,
&wire.StopSendingFrame{}: true,
&wire.AckFrequencyFrame{}: true,
&wire.ImmediateAckFrame{}: true,
}
for f, expected := range testCases {

View File

@@ -55,7 +55,7 @@ func (p *FrameParser) ParseType(b []byte, encLevel protocol.EncryptionLevel) (Fr
valid := ft.isValidRFC9000() ||
(p.supportsDatagrams && ft.IsDatagramFrameType()) ||
(p.supportsResetStreamAt && ft == FrameTypeResetStreamAt) ||
(p.supportsAckFrequency && ft == FrameTypeAckFrequency)
(p.supportsAckFrequency && (ft == FrameTypeAckFrequency || ft == FrameTypeImmediateAck))
if !valid {
return 0, parsed, &qerr.TransportError{
ErrorCode: qerr.FrameEncodingError,
@@ -163,6 +163,8 @@ func (p *FrameParser) ParseLessCommonFrame(frameType FrameType, data []byte, v p
frame, l, err = parseResetStreamFrame(data, true, v)
case FrameTypeAckFrequency:
frame, l, err = parseAckFrequencyFrame(data, v)
case FrameTypeImmediateAck:
frame = &ImmediateAckFrame{}
default:
err = errUnknownFrameType
}

View File

@@ -330,6 +330,11 @@ func TestFrameParserFrames(t *testing.T) {
ReorderingThreshold: 0xcafe,
},
},
{
name: "IMMEDIATE_ACK",
frameType: FrameTypeImmediateAck,
frame: &ImmediateAckFrame{},
},
}
for _, test := range tests {
@@ -523,7 +528,32 @@ func TestFrameParserResetStreamAtUnsupported(t *testing.T) {
require.NoError(t, err)
_, _, err = parser.ParseType(b, protocol.Encryption1RTT)
checkFrameUnsupported(t, err, 0x24)
checkFrameUnsupported(t, err, uint64(FrameTypeResetStreamAt))
}
func TestFrameParserAckFrequencyUnsupported(t *testing.T) {
parser := NewFrameParser(true, true, false)
t.Run("ACK_FREQUENCY", func(t *testing.T) {
f := &AckFrequencyFrame{
SequenceNumber: 1337,
AckElicitingThreshold: 42,
RequestMaxAckDelay: 42 * time.Millisecond,
ReorderingThreshold: 1234,
}
b, err := f.Append(nil, protocol.Version1)
require.NoError(t, err)
_, _, err = parser.ParseType(b, protocol.Encryption1RTT)
checkFrameUnsupported(t, err, uint64(FrameTypeAckFrequency))
})
t.Run("IMMEDIATE_ACK", func(t *testing.T) {
f := &ImmediateAckFrame{}
b, err := f.Append(nil, protocol.Version1)
require.NoError(t, err)
_, _, err = parser.ParseType(b, protocol.Encryption1RTT)
checkFrameUnsupported(t, err, uint64(FrameTypeImmediateAck))
})
}
func TestFrameParserInvalidFrameType(t *testing.T) {

View File

@@ -30,8 +30,11 @@ const (
FrameTypeConnectionClose FrameType = 0x1c
FrameTypeApplicationClose FrameType = 0x1d
FrameTypeHandshakeDone FrameType = 0x1e
FrameTypeResetStreamAt FrameType = 0x24 // https://datatracker.ietf.org/doc/draft-ietf-quic-reliable-stream-reset/06/
FrameTypeAckFrequency FrameType = 0xaf // https://datatracker.ietf.org/doc/draft-ietf-quic-ack-frequency/11/
// https://datatracker.ietf.org/doc/draft-ietf-quic-reliable-stream-reset/07/
FrameTypeResetStreamAt FrameType = 0x24
// https://datatracker.ietf.org/doc/draft-ietf-quic-ack-frequency/11/
FrameTypeAckFrequency FrameType = 0xaf
FrameTypeImmediateAck FrameType = 0x1f
FrameTypeDatagramNoLength FrameType = 0x30
FrameTypeDatagramWithLength FrameType = 0x31

View File

@@ -0,0 +1,18 @@
package wire
import (
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/quicvarint"
)
// An ImmediateAckFrame is an IMMEDIATE_ACK frame
type ImmediateAckFrame struct{}
func (f *ImmediateAckFrame) Append(b []byte, _ protocol.Version) ([]byte, error) {
return quicvarint.Append(b, uint64(FrameTypeImmediateAck)), nil
}
// Length of a written frame
func (f *ImmediateAckFrame) Length(_ protocol.Version) protocol.ByteCount {
return protocol.ByteCount(quicvarint.Len(uint64(FrameTypeImmediateAck)))
}

View File

@@ -0,0 +1,22 @@
package wire
import (
"testing"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/quicvarint"
"github.com/stretchr/testify/require"
)
func TestImmediateAckFrame(t *testing.T) {
frame := ImmediateAckFrame{}
b, err := frame.Append(nil, protocol.Version1)
require.NoError(t, err)
val, l, err := quicvarint.Parse(b)
require.NoError(t, err)
require.Equal(t, uint64(FrameTypeImmediateAck), val)
require.Equal(t, len(b), l)
require.Len(t, b, int(frame.Length(protocol.Version1)))
}