From f612315425e19129665f52208afa53ec71b69892 Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Tue, 25 Aug 2026 20:57:29 -0400 Subject: [PATCH 1/2] fix(client): use andybalholm for Brotli decoding --- go.mod | 3 +-- go.sum | 8 ++++---- integration/integration_test.go | 3 +-- internal/client/client.go | 6 +++--- internal/client/client_test.go | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 9cd25af3..a4cf73af 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/ryanfowler/fetch go 1.27.0 require ( + github.com/andybalholm/brotli v1.2.2 github.com/coder/websocket v1.8.15 github.com/goccy/go-yaml v1.19.2 - github.com/google/brotli/go/brotli v1.1.1-rc0.0.20260804194026-8e10eeb3378f github.com/klauspost/compress v1.19.2 github.com/mattn/go-runewidth v0.0.28 github.com/quic-go/quic-go v0.61.0 @@ -23,7 +23,6 @@ require ( require ( github.com/clipperhouse/uax29/v2 v2.2.0 // indirect - github.com/google/brotli/go/cbrotli v1.1.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect ) diff --git a/go.sum b/go.sum index fd02c015..60ce3085 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/andybalholm/brotli v1.2.2 h1:HzTuoo2ErYQqf5qvcJInB8uvqSVxRttzkFexPWtnceM= +github.com/andybalholm/brotli v1.2.2/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA= @@ -6,10 +8,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/google/brotli/go/brotli v1.1.1-rc0.0.20260804194026-8e10eeb3378f h1:yobCh1TcUKe8picCDfZM9HEmsPWCMMrwG0bTY7YR5Hs= -github.com/google/brotli/go/brotli v1.1.1-rc0.0.20260804194026-8e10eeb3378f/go.mod h1:H2be2ObGAljOfxBqoUED787gaHX8pg0FUqFpX73rxV4= -github.com/google/brotli/go/cbrotli v1.1.0 h1:YwHD/rwSgUSL4b2S3ZM2jnNymm+tmwKQqjUIC63nmHU= -github.com/google/brotli/go/cbrotli v1.1.0/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= @@ -32,6 +30,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ= github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA= github.com/yuin/goldmark v1.8.5/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= diff --git a/integration/integration_test.go b/integration/integration_test.go index 0ed74d79..946c0e52 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -1363,8 +1363,7 @@ func TestMain(t *testing.T) { t.Run("brotli compression", func(t *testing.T) { t.Parallel() const data = "this is the test data" - // The google/brotli module only provides a decoder, so use a fixed - // Brotli stream for the integration server response. + // Use a fixed Brotli stream for the integration server response. const encoded = "\x0b\x0a\x80this is the test data\x03" server := startServer(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/client/client.go b/internal/client/client.go index 13d5d26f..26a48e90 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -24,7 +24,7 @@ import ( "github.com/ryanfowler/fetch/internal/multipart" "github.com/ryanfowler/fetch/internal/resolver" - "github.com/google/brotli/go/brotli" + "github.com/andybalholm/brotli" "github.com/klauspost/compress/gzip" "github.com/klauspost/compress/zstd" "github.com/quic-go/quic-go" @@ -2211,7 +2211,7 @@ func contentEncodingDecoders(h http.Header) ([]namedResponseBodyDecoder, bool) { decoders = append(decoders, namedResponseBodyDecoder{ name: "br", decoder: func(rc io.ReadCloser) (io.ReadCloser, error) { - return &brotliReader{ReadCloser: brotli.NewReader(rc), c: rc}, nil + return &brotliReader{ReadCloser: io.NopCloser(brotli.NewReader(rc)), c: rc}, nil }, }) case "gzip": @@ -2263,7 +2263,7 @@ func contentEncodingDecodersForPolicy(h http.Header, allowed map[string]bool) ([ decoders = append(decoders, namedResponseBodyDecoder{ name: "br", decoder: func(rc io.ReadCloser) (io.ReadCloser, error) { - return &brotliReader{ReadCloser: brotli.NewReader(rc), c: rc}, nil + return &brotliReader{ReadCloser: io.NopCloser(brotli.NewReader(rc)), c: rc}, nil }, }) case "gzip": diff --git a/internal/client/client_test.go b/internal/client/client_test.go index 8800908e..2d6020f2 100644 --- a/internal/client/client_test.go +++ b/internal/client/client_test.go @@ -389,8 +389,8 @@ func TestDoClosesResponseBodyWhenDecoderConstructionFails(t *testing.T) { func TestDoDecodesBrotliContentEncoding(t *testing.T) { const data = "this is Brotli encoded data" - // The google/brotli module only provides a decoder. Keep the encoded - // fixture constant so this test does not need a second Brotli dependency. + // Keep the encoded fixture constant so this test does not need a second + // Brotli dependency. body := []byte("\x0b\x0d\x80this is Brotli encoded data\x03") c := &Client{ c: &http.Client{ From 28babb346612c6c4073931f9f953c4cd63cf1670 Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Tue, 25 Aug 2026 21:03:00 -0400 Subject: [PATCH 2/2] test(fetch): make clipboard timeout deterministic --- internal/fetch/clipboard_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/fetch/clipboard_test.go b/internal/fetch/clipboard_test.go index 6589428a..becd1c0c 100644 --- a/internal/fetch/clipboard_test.go +++ b/internal/fetch/clipboard_test.go @@ -52,7 +52,6 @@ func TestClipboardCommandHelper(t *testing.T) { if err := cmd.Start(); err != nil { t.Fatalf("unable to start clipboard descendant: %v", err) } - os.Exit(0) } if os.Getenv("FETCH_TEST_CLIPBOARD_BLOCK") == "1" { select {} @@ -68,8 +67,11 @@ func TestClipboardCommandHelper(t *testing.T) { } func TestCopyToClipboardTimeout(t *testing.T) { + // Keep both processes alive so the test does not depend on the platform's + // stdin pipe capacity. t.Setenv("FETCH_TEST_CLIPBOARD_HELPER", "1") t.Setenv("FETCH_TEST_CLIPBOARD_FORK", "1") + t.Setenv("FETCH_TEST_CLIPBOARD_BLOCK", "1") start := time.Now() err := copyToClipboardWithTimeout(context.Background(), &clipboardCmd{