Repository: quic-go/quic-go
Pull request #5070 (2025-04-20 13:21:36)

http3: simplify request writer by writing to an io.Writer
~~Depends on #5069.~~

It’s always preferable to use the most minimal interface.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/quic-go/quic-go/pull/5070?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go) Report
Attention: Patch coverage is `50.00000%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 84.05%. Comparing base [(`ef5126c`)](https://app.codecov.io/gh/quic-go/quic-go/commit/ef5126c23771565d42022d6605dc25239f1d1317?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go) to head [(`880611c`)](https://app.codecov.io/gh/quic-go/quic-go/commit/880611c4bc10099169aeac5099abb917c76b7166?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go).

| [Files with missing lines](https://app.codecov.io/gh/quic-go/quic-go/pull/5070?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go) | Patch % | Lines |
|---|---|---|
| [http3/request\_writer.go](https://app.codecov.io/gh/quic-go/quic-go/pull/5070?src=pr&el=tree&filepath=http3%2Frequest_writer.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go#diff-aHR0cDMvcmVxdWVzdF93cml0ZXIuZ28=) | 50.00% | [0 Missing and 1 partial :warning: ](https://app.codecov.io/gh/quic-go/quic-go/pull/5070?src=pr&el=tree&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                            @@
##           translate-http3-request-writer-tests    #5070      +/-   ##
========================================================================
- Coverage                                 84.19%   84.05%   -0.15%     
========================================================================
  Files                                       153      150       -3     
  Lines                                     16993    16648     -345     
========================================================================
- Hits                                      14307    13992     -315     
+ Misses                                     2156     2140      -16     
+ Partials                                    530      516      -14     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/quic-go/quic-go/pull/5070?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/http3/request_writer.go b/http3/request_writer.go
index 2dbacb15..6d473aa6 100644
--- a/http3/request_writer.go
+++ b/http3/request_writer.go
@@ -17,7 +17,6 @@ import (
 	"golang.org/x/net/idna"
 
 	"github.com/quic-go/qpack"
-	"github.com/quic-go/quic-go"
 )
 
 const bodyCopyBufferSize = 8 * 1024
@@ -37,13 +36,13 @@ func newRequestWriter() *requestWriter {
 	}
 }
 
-func (w *requestWriter) WriteRequestHeader(str quic.Stream, req *http.Request, gzip bool) error {
+func (w *requestWriter) WriteRequestHeader(wr io.Writer, req *http.Request, gzip bool) error {
 	// TODO: figure out how to add support for trailers
 	buf := &bytes.Buffer{}
 	if err := w.writeHeaders(buf, req, gzip); err != nil {
 		return err
 	}
-	if _, err := str.Write(buf.Bytes()); err != nil {
+	if _, err := wr.Write(buf.Bytes()); err != nil {
 		return err
 	}
 	trace := httptrace.ContextClientTrace(req.Context())


Test patch:
diff --git a/http3/request_writer_test.go b/http3/request_writer_test.go
index 5e5d7bd4..85ee512b 100644
--- a/http3/request_writer_test.go
+++ b/http3/request_writer_test.go
@@ -7,10 +7,8 @@ import (
 	"testing"
 
 	"github.com/quic-go/qpack"
-	mockquic "github.com/quic-go/quic-go/internal/mocks/quic"
 
 	"github.com/stretchr/testify/require"
-	"go.uber.org/mock/gomock"
 )
 
 func decodeRequest(t *testing.T, str io.Reader) map[string]string {
@@ -49,12 +47,9 @@ func testRequestWriterGzip(t *testing.T, gzip bool) {
 	req.AddCookie(&http.Cookie{Name: "baz", Value: "lorem ipsum"})
 
 	rw := newRequestWriter()
-	strBuf := &bytes.Buffer{}
-	mockCtrl := gomock.NewController(t)
-	str := mockquic.NewMockStream(mockCtrl)
-	str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
-	require.NoError(t, rw.WriteRequestHeader(str, req, gzip))
-	headerFields := decodeRequest(t, strBuf)
+	buf := &bytes.Buffer{}
+	require.NoError(t, rw.WriteRequestHeader(buf, req, gzip))
+	headerFields := decodeRequest(t, buf)
 	require.Equal(t, "quic-go.net", headerFields[":authority"])
 	require.Equal(t, http.MethodGet, headerFields[":method"])
 	require.Equal(t, "/index.html?foo=bar", headerFields[":path"])
@@ -73,12 +68,8 @@ func TestRequestWriterInvalidHostHeader(t *testing.T) {
 	require.NoError(t, err)
 	req.Host = "foo@bar" // @ is invalid
 	rw := newRequestWriter()
-	strBuf := &bytes.Buffer{}
-	mockCtrl := gomock.NewController(t)
-	str := mockquic.NewMockStream(mockCtrl)
-	str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
 	require.EqualError(t,
-		rw.WriteRequestHeader(str, req, false),
+		rw.WriteRequestHeader(&bytes.Buffer{}, req, false),
 		"http3: invalid Host header",
 	)
 }
@@ -87,12 +78,9 @@ func TestRequestWriterConnect(t *testing.T) {
 	req, err := http.NewRequest(http.MethodConnect, "https://quic-go.net/", nil)
 	require.NoError(t, err)
 	rw := newRequestWriter()
-	strBuf := &bytes.Buffer{}
-	mockCtrl := gomock.NewController(t)
-	str := mockquic.NewMockStream(mockCtrl)
-	str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
-	require.NoError(t, rw.WriteRequestHeader(str, req, false))
-	headerFields := decodeRequest(t, strBuf)
+	buf := &bytes.Buffer{}
+	require.NoError(t, rw.WriteRequestHeader(buf, req, false))
+	headerFields := decodeRequest(t, buf)
 	require.Equal(t, http.MethodConnect, headerFields[":method"])
 	require.Equal(t, "quic-go.net", headerFields[":authority"])
 	require.NotContains(t, headerFields, ":path")
@@ -105,12 +93,9 @@ func TestRequestWriterExtendedConnect(t *testing.T) {
 	require.NoError(t, err)
 	req.Proto = "webtransport"
 	rw := newRequestWriter()
-	strBuf := &bytes.Buffer{}
-	mockCtrl := gomock.NewController(t)
-	str := mockquic.NewMockStream(mockCtrl)
-	str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
-	require.NoError(t, rw.WriteRequestHeader(str, req, false))
-	headerFields := decodeRequest(t, strBuf)
+	buf := &bytes.Buffer{}
+	require.NoError(t, rw.WriteRequestHeader(buf, req, false))
+	headerFields := decodeRequest(t, buf)
 	require.Equal(t, "quic-go.net", headerFields[":authority"])
 	require.Equal(t, http.MethodConnect, headerFields[":method"])
 	require.Equal(t, "/", headerFields[":path"])