Skip to content

Commit 005d243

Browse files
Merge pull request #4320 from ipfs/fix/gateway-seeker
gateway: fix seeker can't seek on specific files
2 parents 3119f63 + cbccd84 commit 005d243

File tree

6 files changed

+66
-6
lines changed

6 files changed

+66
-6
lines changed

core/corehttp/gateway_handler.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
268268

269269
if !dir {
270270
name := gopath.Base(urlPath)
271-
http.ServeContent(w, r, name, modtime, dr)
271+
i.serveFile(w, r, name, modtime, dr)
272272
return
273273
}
274274

@@ -372,6 +372,34 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
372372
}
373373
}
374374

375+
type sizeReadSeeker interface {
376+
Size() uint64
377+
378+
io.ReadSeeker
379+
}
380+
381+
type sizeSeeker struct {
382+
sizeReadSeeker
383+
}
384+
385+
func (s *sizeSeeker) Seek(offset int64, whence int) (int64, error) {
386+
if whence == io.SeekEnd && offset == 0 {
387+
return int64(s.Size()), nil
388+
}
389+
390+
return s.sizeReadSeeker.Seek(offset, whence)
391+
}
392+
393+
func (i *gatewayHandler) serveFile(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, content io.ReadSeeker) {
394+
if sp, ok := content.(sizeReadSeeker); ok {
395+
content = &sizeSeeker{
396+
sizeReadSeeker: sp,
397+
}
398+
}
399+
400+
http.ServeContent(w, req, name, modtime, content)
401+
}
402+
375403
func (i *gatewayHandler) postHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
376404
p, err := i.api.Unixfs().Add(ctx, r.Body)
377405
if err != nil {

merkledag/merkledag.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -307,11 +307,14 @@ func GetNodes(ctx context.Context, ds DAGService, keys []*cid.Cid) []NodeGetter
307307

308308
// Remove duplicates from a list of keys
309309
func dedupeKeys(cids []*cid.Cid) []*cid.Cid {
310+
out := make([]*cid.Cid, 0, len(cids))
310311
set := cid.NewSet()
311312
for _, c := range cids {
312-
set.Add(c)
313+
if set.Visit(c) {
314+
out = append(out, c)
315+
}
313316
}
314-
return set.Keys()
317+
return out
315318
}
316319

317320
func newNodePromise(ctx context.Context) NodeGetter {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
foo
82 Bytes
Binary file not shown.

test/sharness/t0110-gateway.sh

+13-2
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ test_expect_success "HEAD 'index.html' has no content" '
124124
# test ipfs readonly api
125125

126126
test_curl_gateway_api() {
127-
curl -sfo actual "http://127.0.0.1:$port/api/v0/$1"
127+
curl -sfo actual "http://127.0.0.1:$port/api/v0/$1"
128128
}
129129

130130
test_expect_success "get IPFS directory file through readonly API succeeds" '
@@ -140,7 +140,7 @@ test_expect_success "refs IPFS directory file through readonly API succeeds" '
140140
'
141141

142142
test_expect_success "test gateway api is sanitized" '
143-
for cmd in "add" "block/put" "bootstrap" "config" "dht" "diag" "dns" "get" "id" "mount" "name/publish" "object/put" "object/new" "object/patch" "pin" "ping" "refs/local" "repo" "resolve" "stats" "swarm" "file" "update" "version" "bitswap"; do
143+
for cmd in "add" "block/put" "bootstrap" "config" "dht" "diag" "dns" "get" "id" "mount" "name/publish" "object/put" "object/new" "object/patch" "pin" "ping" "refs/local" "repo" "resolve" "stats" "swarm" "file" "update" "version" "bitswap"; do
144144
test_curl_resp_http_code "http://127.0.0.1:$port/api/v0/$cmd" "HTTP/1.1 404 Not Found"
145145
done
146146
'
@@ -155,6 +155,17 @@ test_expect_success "try fetching it from gateway" '
155155
test_cmp rfile ffile
156156
'
157157

158+
test_expect_success "Add compact blocks" '
159+
ipfs block put ../t0110-gateway-data/foo.block &&
160+
FOO2_HASH=$(ipfs block put ../t0110-gateway-data/foofoo.block) &&
161+
printf "foofoo" > expected
162+
'
163+
164+
test_expect_success "GET compact blocks succeeds" '
165+
curl -o actual "http://127.0.0.1:$port/ipfs/$FOO2_HASH" &&
166+
test_cmp expected actual
167+
'
168+
158169
test_kill_ipfs_daemon
159170

160171
test_done

unixfs/io/pbdagreader.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) {
188188
if offset < 0 {
189189
return -1, errors.New("Invalid offset")
190190
}
191+
if offset == dr.offset {
192+
return offset, nil
193+
}
191194

192195
// Grab cached protobuf object (solely to make code look cleaner)
193196
pb := dr.pbdata
@@ -239,11 +242,24 @@ func (dr *pbDagReader) Seek(offset int64, whence int) (int64, error) {
239242
return offset, nil
240243
case io.SeekCurrent:
241244
// TODO: be smarter here
245+
if offset == 0 {
246+
return dr.offset, nil
247+
}
248+
242249
noffset := dr.offset + offset
243250
return dr.Seek(noffset, io.SeekStart)
244251
case io.SeekEnd:
245252
noffset := int64(dr.pbdata.GetFilesize()) - offset
246-
return dr.Seek(noffset, io.SeekStart)
253+
n, err := dr.Seek(noffset, io.SeekStart)
254+
255+
// Return negative number if we can't figure out the file size. Using io.EOF
256+
// for this seems to be good(-enough) solution as it's only returned by
257+
// precalcNextBuf when we step out of file range.
258+
// This is needed for gateway to function properly
259+
if err == io.EOF && *dr.pbdata.Type == ftpb.Data_File {
260+
return -1, nil
261+
}
262+
return n, err
247263
default:
248264
return 0, errors.New("invalid whence")
249265
}

0 commit comments

Comments
 (0)