From 846f571eb739bd8926daaa6d1056f96b68f5f35d Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 17 Jul 2026 08:46:42 +0100 Subject: [PATCH] imagekit: fix Open with a RangeOption returning the wrong data Open decoded range options against an unknown size, producing a negative offset for suffix ranges (eg the last N bytes) and sending syntactically invalid Range headers which the server ignored. A suffix range then returned the whole file instead of the requested tail. Decode the options against the known object size so the offset is always absolute, only send a Range header when one was requested, and honour the requested count when the server ignores the Range header. --- backend/imagekit/imagekit.go | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/backend/imagekit/imagekit.go b/backend/imagekit/imagekit.go index 6a72422c8..0c8521d0d 100644 --- a/backend/imagekit/imagekit.go +++ b/backend/imagekit/imagekit.go @@ -593,12 +593,12 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo var offset int64 var count int64 - fs.FixRangeOption(options, -1) + fs.FixRangeOption(options, o.Size()) partialContent := false for _, option := range options { switch x := option.(type) { case *fs.RangeOption: - offset, count = x.Decode(-1) + offset, count = x.Decode(o.Size()) partialContent = true case *fs.SeekOption: offset = x.Offset @@ -626,7 +626,13 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) - req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+count-1)) + if partialContent { + if count > 0 { + req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+count-1)) + } else { + req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset)) + } + } resp, err := client.Do(req) if err != nil { @@ -636,13 +642,9 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo end := resp.ContentLength if partialContent && resp.StatusCode == http.StatusOK { - skip := offset - - if offset < 0 { - skip = end + offset + 1 - } - - _, err = io.CopyN(io.Discard, resp.Body, skip) + // The server ignored the Range request so skip to the + // offset and limit what is read ourselves + _, err = io.CopyN(io.Discard, resp.Body, offset) if err != nil { if resp != nil { _ = resp.Body.Close() @@ -650,7 +652,11 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo return nil, err } - return readers.NewLimitedReadCloser(resp.Body, end-skip), nil + limit := end - offset + if count > 0 && count < limit { + limit = count + } + return readers.NewLimitedReadCloser(resp.Body, limit), nil } return resp.Body, nil