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.
This commit is contained in:
Nick Craig-Wood
2026-07-17 18:29:39 +01:00
parent d6190dc4f2
commit 846f571eb7
+16 -10
View File
@@ -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)
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