Repository: quic-go/quic-go
Pull request #4901 (2025-01-21 05:51:52)

wire: implement frame classification into probing / non-probing
Needed for #234. Closes #4866.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/quic-go/quic-go/pull/4901?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 83.49%. Comparing base [(`33fd4d1`)](https://app.codecov.io/gh/quic-go/quic-go/commit/33fd4d1d16ab503b70bdef8fcc40a2579e1481ce?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go) to head [(`b8014dd`)](https://app.codecov.io/gh/quic-go/quic-go/commit/b8014dde066d571a76d4901e3fc965efe56ccc8e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go).
> Report is 1 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #4901      +/-   ##
==========================================
- Coverage   83.60%   83.49%   -0.11%     
==========================================
  Files         151      149       -2     
  Lines       16221    15881     -340     
==========================================
- Hits        13561    13259     -302     
+ Misses       2132     2110      -22     
+ Partials      528      512      -16     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/quic-go/quic-go/pull/4901?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go).


Patch:
diff --git a/fuzzing/frames/fuzz.go b/fuzzing/frames/fuzz.go
index 7229f488..0f4e9033 100644
--- a/fuzzing/frames/fuzz.go
+++ b/fuzzing/frames/fuzz.go
@@ -3,6 +3,7 @@ package frames
 import (
 	"fmt"
 
+	"github.com/quic-go/quic-go/internal/ackhandler"
 	"github.com/quic-go/quic-go/internal/protocol"
 	"github.com/quic-go/quic-go/internal/wire"
 )
@@ -49,6 +50,8 @@ func Fuzz(data []byte) int {
 		if f == nil { // PADDING frame
 			continue
 		}
+		wire.IsProbingFrame(f)
+		ackhandler.IsFrameAckEliciting(f)
 		// We accept empty STREAM frames, but we don't write them.
 		if sf, ok := f.(*wire.StreamFrame); ok {
 			if sf.DataLen() == 0 {
diff --git a/internal/wire/frame.go b/internal/wire/frame.go
new file mode 100644
index 00000000..10d4eebc
--- /dev/null
+++ b/internal/wire/frame.go
@@ -0,0 +1,21 @@
+package wire
+
+import (
+	"github.com/quic-go/quic-go/internal/protocol"
+)
+
+// A Frame in QUIC
+type Frame interface {
+	Append(b []byte, version protocol.Version) ([]byte, error)
+	Length(version protocol.Version) protocol.ByteCount
+}
+
+// IsProbingFrame returns true if the frame is a probing frame.
+// See section 9.1 of RFC 9000.
+func IsProbingFrame(f Frame) bool {
+	switch f.(type) {
+	case *PathChallengeFrame, *PathResponseFrame, *NewConnectionIDFrame:
+		return true
+	}
+	return false
+}
diff --git a/internal/wire/interface.go b/internal/wire/interface.go
deleted file mode 100644
index bc17883b..00000000
--- a/internal/wire/interface.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package wire
-
-import (
-	"github.com/quic-go/quic-go/internal/protocol"
-)
-
-// A Frame in QUIC
-type Frame interface {
-	Append(b []byte, version protocol.Version) ([]byte, error)
-	Length(version protocol.Version) protocol.ByteCount
-}


Test patch:
diff --git a/http3/transport_test.go b/http3/transport_test.go
index a2f90378..2ddc83cb 100644
--- a/http3/transport_test.go
+++ b/http3/transport_test.go
@@ -221,7 +221,7 @@ func TestTransportConnectionReuse(t *testing.T) {
 	conn := mockquic.NewMockEarlyConnection(mockCtrl)
 	handshakeChan := make(chan struct{})
 	close(handshakeChan)
-	conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2)
+	conn.EXPECT().HandshakeComplete().Return(handshakeChan).AnyTimes()
 	var dialCount int
 	tr := &Transport{
 		Dial: func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) {
@@ -342,7 +342,7 @@ func TestTransportRequestContextCancellation(t *testing.T) {
 	conn := mockquic.NewMockEarlyConnection(mockCtrl)
 	handshakeChan := make(chan struct{})
 	close(handshakeChan)
-	conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(3)
+	conn.EXPECT().HandshakeComplete().Return(handshakeChan).AnyTimes()
 	var dialCount int
 	tr := &Transport{
 		Dial: func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) {
@@ -389,7 +389,7 @@ func TestTransportConnetionRedialHandshakeError(t *testing.T) {
 	conn := mockquic.NewMockEarlyConnection(mockCtrl)
 	handshakeChan := make(chan struct{})
 	close(handshakeChan)
-	conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2)
+	conn.EXPECT().HandshakeComplete().Return(handshakeChan).AnyTimes()
 	var dialCount int
 	testErr := errors.New("handshake error")
 	tr := &Transport{
diff --git a/internal/wire/frame_test.go b/internal/wire/frame_test.go
new file mode 100644
index 00000000..3012b558
--- /dev/null
+++ b/internal/wire/frame_test.go
@@ -0,0 +1,29 @@
+package wire
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestProbingFrames(t *testing.T) {
+	testCases := map[Frame]bool{
+		&AckFrame{}:             false,
+		&ConnectionCloseFrame{}: false,
+		&DataBlockedFrame{}:     false,
+		&PingFrame{}:            false,
+		&ResetStreamFrame{}:     false,
+		&StreamFrame{}:          false,
+		&DatagramFrame{}:        false,
+		&MaxDataFrame{}:         false,
+		&MaxStreamDataFrame{}:   false,
+		&StopSendingFrame{}:     false,
+		&PathChallengeFrame{}:   true,
+		&PathResponseFrame{}:    true,
+		&NewConnectionIDFrame{}: true,
+	}
+
+	for f, expected := range testCases {
+		require.Equal(t, expected, IsProbingFrame(f))
+	}
+}