Repository: quic-go/quic-go
Pull request #5196 (2025-06-07 03:13:11)

http3: simplify connection closing in the frame parser
No functional change expected.

This will make it easier to convert the `quic.Connection` to a struct, see #4968.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/quic-go/quic-go/pull/5196?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.62%. Comparing base [(`08be0f7`)](https://app.codecov.io/gh/quic-go/quic-go/commit/08be0f73c0a9370d8aacff1c451e50d318f0034c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go) to head [(`be973b2`)](https://app.codecov.io/gh/quic-go/quic-go/commit/be973b21593a251628da29d82d2c074b5aa3ea3e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go).

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


```diff
@@            Coverage Diff             @@
##           master    #5196      +/-   ##
==========================================
- Coverage   83.87%   83.62%   -0.25%     
==========================================
  Files         154      150       -4     
  Lines       17432    17268     -164     
==========================================
- Hits        14620    14440     -180     
- Misses       2242     2261      +19     
+ Partials      570      567       -3     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/quic-go/quic-go/pull/5196?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).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 2ce5cf57..844f64d0 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -9,8 +9,6 @@ jobs:
       - uses: actions/setup-go@v5
         with:
           go-version: "1.24.x"
-      - name: Check that no non-test files import Ginkgo or Gomega
-        run: .github/workflows/no_ginkgo.sh
       - name: Check for //go:build ignore in .go files
         run: |
           IGNORED_FILES=$(grep -rl '//go:build ignore' . --include='*.go') || true
diff --git a/.github/workflows/no_ginkgo.sh b/.github/workflows/no_ginkgo.sh
deleted file mode 100755
index 8857d98a..00000000
--- a/.github/workflows/no_ginkgo.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-# Verify that no non-test files import Ginkgo or Gomega.
-
-set -e
-
-HAS_TESTING=false
-
-cd ..
-for f in $(find . -name "*.go" ! -name "*_test.go" ! -name "tools.go"); do
-	if grep -q "github.com/onsi/ginkgo" $f; then
-    echo "$f imports github.com/onsi/ginkgo/v2"
-    HAS_TESTING=true
-	fi
-	if grep -q "github.com/onsi/gomega" $f; then
-    echo "$f imports github.com/onsi/gomega"
-    HAS_TESTING=true
-	fi
-done
-
-if "$HAS_TESTING"; then
-	exit 1
-fi
-exit 0
diff --git a/http3/client.go b/http3/client.go
index d9666bb7..fa6520a7 100644
--- a/http3/client.go
+++ b/http3/client.go
@@ -155,8 +155,8 @@ func (c *ClientConn) handleBidirectionalStreams(streamHijacker func(FrameType, q
 			return
 		}
 		fp := &frameParser{
-			r:    str,
-			conn: &c.connection,
+			r:         str,
+			closeConn: c.CloseWithError,
 			unknownFrameHandler: func(ft FrameType, e error) (processed bool, err error) {
 				id := c.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID)
 				return streamHijacker(ft, id, str, e)
diff --git a/http3/conn.go b/http3/conn.go
index 61a83745..f3bcecd8 100644
--- a/http3/conn.go
+++ b/http3/conn.go
@@ -287,7 +287,7 @@ func (c *connection) handleUnidirectionalStreams(hijack func(StreamType, quic.Co
 }
 
 func (c *connection) handleControlStream(str *quic.ReceiveStream) {
-	fp := &frameParser{conn: c.Connection, r: str}
+	fp := &frameParser{closeConn: c.Connection.CloseWithError, r: str}
 	f, err := fp.ParseNext()
 	if err != nil {
 		var serr *quic.StreamError
diff --git a/http3/frames.go b/http3/frames.go
index b54afb31..e5149cb2 100644
--- a/http3/frames.go
+++ b/http3/frames.go
@@ -21,7 +21,7 @@ var errHijacked = errors.New("hijacked")
 
 type frameParser struct {
 	r                   io.Reader
-	conn                quic.Connection
+	closeConn           func(quic.ApplicationErrorCode, string) error
 	unknownFrameHandler unknownFrameHandlerFunc
 }
 
@@ -70,7 +70,7 @@ func (p *frameParser) ParseNext() (frame, error) {
 			return parseGoAwayFrame(qr, l)
 		case 0xd: // MAX_PUSH_ID
 		case 0x2, 0x6, 0x8, 0x9:
-			p.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "")
+			p.closeConn(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "")
 			return nil, fmt.Errorf("http3: reserved frame type: %d", t)
 		}
 		// skip over unknown frames
diff --git a/http3/server.go b/http3/server.go
index 85f1356b..8884650c 100644
--- a/http3/server.go
+++ b/http3/server.go
@@ -562,7 +562,7 @@ func (s *Server) handleRequest(conn *connection, str datagramStream, decoder *qp
 			)
 		}
 	}
-	fp := &frameParser{conn: conn, r: str, unknownFrameHandler: ufh}
+	fp := &frameParser{closeConn: conn.CloseWithError, r: str, unknownFrameHandler: ufh}
 	frame, err := fp.ParseNext()
 	if err != nil {
 		if !errors.Is(err, errHijacked) {
diff --git a/http3/stream.go b/http3/stream.go
index 05f3c108..b13fbfd8 100644
--- a/http3/stream.go
+++ b/http3/stream.go
@@ -54,8 +54,8 @@ func newStream(str datagramStream, conn *connection, parseTrailer func(io.Reader
 
 func (s *Stream) Read(b []byte) (int, error) {
 	fp := &frameParser{
-		r:    s.datagramStream,
-		conn: s.conn,
+		r:         s.datagramStream,
+		closeConn: s.conn.CloseWithError,
 	}
 	if s.bytesRemainingInFrame == 0 {
 	parseLoop:
@@ -196,7 +196,7 @@ func (s *RequestStream) SendRequestHeader(req *http.Request) error {
 func (s *RequestStream) ReadResponse() (*http.Response, error) {
 	qstr := s.datagramStream
 	fp := &frameParser{
-		conn: s.conn,
+		closeConn: s.conn.CloseWithError,
 		r: &tracingReader{
 			Reader: qstr,
 			first:  &s.firstByte,
diff --git a/internal/mocks/crypto_setup.go b/internal/mocks/crypto_setup.go
index a4feaeb8..e1b4b627 100644
--- a/internal/mocks/crypto_setup.go
+++ b/internal/mocks/crypto_setup.go
@@ -3,7 +3,7 @@
 //
 // Generated by this command:
 //
-//	mockgen -typed -build_flags=-tags=gomock -package mocks -destination crypto_setup_tmp.go github.com/quic-go/quic-go/internal/handshake CryptoSetup
+//	mockgen -typed -build_flags=-tags=gomock -package mocks -destination crypto_setup.go github.com/quic-go/quic-go/internal/handshake CryptoSetup
 //
 
 // Package mocks is a generated GoMock package.
diff --git a/internal/mocks/mockgen.go b/internal/mocks/mockgen.go
index 87c95fdf..edc58f4e 100644
--- a/internal/mocks/mockgen.go
+++ b/internal/mocks/mockgen.go
@@ -5,7 +5,7 @@ package mocks
 //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination short_header_sealer.go github.com/quic-go/quic-go/internal/handshake ShortHeaderSealer"
 //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination short_header_opener.go github.com/quic-go/quic-go/internal/handshake ShortHeaderOpener"
 //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination long_header_opener.go github.com/quic-go/quic-go/internal/handshake LongHeaderOpener"
-//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination crypto_setup_tmp.go github.com/quic-go/quic-go/internal/handshake CryptoSetup && sed -E 's~github.com/quic-go/qtls[[:alnum:]_-]*~github.com/quic-go/quic-go/internal/qtls~g; s~qtls.ConnectionStateWith0RTT~qtls.ConnectionState~g' crypto_setup_tmp.go > crypto_setup.go && rm crypto_setup_tmp.go && go run golang.org/x/tools/cmd/goimports -w crypto_setup.go"
+//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination crypto_setup.go github.com/quic-go/quic-go/internal/handshake CryptoSetup"
 //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination stream_flow_controller.go github.com/quic-go/quic-go/internal/flowcontrol StreamFlowController"
 //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination congestion.go github.com/quic-go/quic-go/internal/congestion SendAlgorithmWithDebugInfos"
 //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination connection_flow_controller.go github.com/quic-go/quic-go/internal/flowcontrol ConnectionFlowController"


Test patch:
diff --git a/http3/frames_test.go b/http3/frames_test.go
index f8e20f33..dcb317b2 100644
--- a/http3/frames_test.go
+++ b/http3/frames_test.go
@@ -37,8 +37,8 @@ func TestParserReservedFrameType(t *testing.T) {
 			data = append(data, []byte("foobar")...)
 
 			fp := frameParser{
-				r:    bytes.NewReader(data),
-				conn: client,
+				r:         bytes.NewReader(data),
+				closeConn: client.CloseWithError,
 			}
 			_, err := fp.ParseNext()
 			require.Error(t, err)