Skip to content

Commit cf2698c

Browse files
committed
Fix gzip decoding of HTTP sources.
The go http library will normally implicitly set an Accept-Encoding of gzip to requests and then transparently decompress the response body. However, if the Accept-Encoding header is explicitly set by the caller it will no longer transparently decompress the body. This was causing HTTP LLB sources to have unexpectedly compressed contents. The fix here just unsets the Accept-Encoding header after the HEAD request. Another possible fix would be to do our own gzip decompression after reading the body if it was gzipped, but this approach seemed slightly simpler. Signed-off-by: Erik Sipsma <[email protected]>
1 parent 3187d2d commit cf2698c

File tree

3 files changed

+57
-4
lines changed

3 files changed

+57
-4
lines changed

client/client_test.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package client
33
import (
44
"archive/tar"
55
"bytes"
6+
"compress/gzip"
67
"context"
78
"crypto/rand"
89
"crypto/rsa"
@@ -2196,6 +2197,49 @@ func testBuildHTTPSource(t *testing.T, sb integration.Sandbox) {
21962197
require.Equal(t, http.MethodHead, allReqs[1].Method)
21972198
require.Equal(t, "gzip", allReqs[1].Header.Get("Accept-Encoding"))
21982199

2200+
require.NoError(t, os.RemoveAll(filepath.Join(tmpdir, "foo")))
2201+
2202+
// update the content at the url to be gzipped now, the final output
2203+
// should remain the same
2204+
modTime = time.Now().Add(-23 * time.Hour)
2205+
var buf bytes.Buffer
2206+
gw := gzip.NewWriter(&buf)
2207+
_, err = gw.Write(resp.Content)
2208+
require.NoError(t, err)
2209+
require.NoError(t, gw.Close())
2210+
gzipBytes := buf.Bytes()
2211+
respGzip := httpserver.Response{
2212+
Etag: identity.NewID(),
2213+
Content: gzipBytes,
2214+
LastModified: &modTime,
2215+
ContentEncoding: "gzip",
2216+
}
2217+
server.SetRoute("/foo", respGzip)
2218+
2219+
_, err = c.Solve(sb.Context(), def, SolveOpt{
2220+
Exports: []ExportEntry{
2221+
{
2222+
Type: ExporterLocal,
2223+
OutputDir: tmpdir,
2224+
},
2225+
},
2226+
}, nil)
2227+
require.NoError(t, err)
2228+
2229+
require.Equal(t, server.Stats("/foo").AllRequests, 4)
2230+
require.Equal(t, server.Stats("/foo").CachedRequests, 1)
2231+
2232+
dt, err = os.ReadFile(filepath.Join(tmpdir, "foo"))
2233+
require.NoError(t, err)
2234+
require.Equal(t, resp.Content, dt)
2235+
2236+
allReqs = server.Stats("/foo").Requests
2237+
require.Equal(t, 4, len(allReqs))
2238+
require.Equal(t, http.MethodHead, allReqs[2].Method)
2239+
require.Equal(t, "gzip", allReqs[2].Header.Get("Accept-Encoding"))
2240+
require.Equal(t, http.MethodGet, allReqs[3].Method)
2241+
require.Equal(t, "gzip", allReqs[3].Header.Get("Accept-Encoding"))
2242+
21992243
// test extra options
22002244
st = llb.HTTP(server.URL+"/foo", llb.Filename("bar"), llb.Chmod(0741), llb.Chown(1000, 1000))
22012245

@@ -2212,7 +2256,7 @@ func testBuildHTTPSource(t *testing.T, sb integration.Sandbox) {
22122256
}, nil)
22132257
require.NoError(t, err)
22142258

2215-
require.Equal(t, server.Stats("/foo").AllRequests, 3)
2259+
require.Equal(t, server.Stats("/foo").AllRequests, 5)
22162260
require.Equal(t, server.Stats("/foo").CachedRequests, 1)
22172261

22182262
dt, err = os.ReadFile(filepath.Join(tmpdir, "bar"))

source/http/httpsource.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ func (hs *httpSourceHandler) CacheKey(ctx context.Context, g session.Group, inde
205205
resp.Body.Close()
206206
}
207207
req.Method = "GET"
208+
// Unset explicit Accept-Encoding for GET, otherwise the go http library will not
209+
// transparently decompress the response body when it is gzipped. It will still add
210+
// this header implicitly when the request is made though.
211+
req.Header.Del("Accept-Encoding")
208212
}
209213

210214
resp, err := client.Do(req)

util/testutil/httpserver/server.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ func (s *TestServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
5151
w.Header().Set("Last-Modified", resp.LastModified.Format(time.RFC850))
5252
}
5353

54+
if resp.ContentEncoding != "" {
55+
w.Header().Set("Content-Encoding", resp.ContentEncoding)
56+
}
57+
5458
if resp.Etag != "" {
5559
w.Header().Set("ETag", resp.Etag)
5660
if match := r.Header.Get("If-None-Match"); match == resp.Etag {
@@ -75,9 +79,10 @@ func (s *TestServer) Stats(name string) (st Stat) {
7579
}
7680

7781
type Response struct {
78-
Content []byte
79-
Etag string
80-
LastModified *time.Time
82+
Content []byte
83+
Etag string
84+
LastModified *time.Time
85+
ContentEncoding string
8186
}
8287

8388
type Stat struct {

0 commit comments

Comments
 (0)