@cryptotaxi247 / kubo / commits / 3859f08bf

fix(gateway): better seeking/sized

1. Require files to have known sizes. We can add support for unknown sizes _later_ but we can't use ServeContent for those files. 2. Replace the `sizeReadSeeker` with a `lazySeeker`. This one makes no assumptions about how it's used so we're less likely to run into weird bugs.

Steven Allen committed Sep 26, 2019 at 13:26 UTC 3859f08bf79c9a68933c9d398f834c5c32462664
2 files changed +69 -19
core/corehttp/gateway_handler.go
+9 -19
@@ -372,29 +372,19 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
372 }
373 }
374
375 -type sizeReadSeeker interface {
375 +type sized interface {
376 Size() (int64, error)
377 -
378 - io.ReadSeeker
379 -}
380 -
381 -type sizeSeeker struct {
382 - sizeReadSeeker
377 }
378
385 -func (s *sizeSeeker) Seek(offset int64, whence int) (int64, error) {
386 - if whence == io.SeekEnd && offset == 0 {
387 - return s.Size()
379 +func (i *gatewayHandler) serveFile(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, file files.File) {
380 + size, err := file.Size()
381 + if err != nil {
382 + http.Error(w, "cannot serve files with unknown sizes", http.StatusBadGateway)
383 + return
384 }
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 - }
385 + content := &lazySeeker{
386 + size: size,
387 + reader: file,
388 }
389
390 ctype := mime.TypeByExtension(gopath.Ext(name))
core/corehttp/lazyseek.go new
+60
@@ -0,0 +1,60 @@
1 +package corehttp
2 +
3 +import (
4 + "fmt"
5 + "io"
6 +)
7 +
8 +// The HTTP server uses seek to determine the file size. Actually _seeking_ can
9 +// be slow so we wrap the seeker in a _lazy_ seeker.
10 +type lazySeeker struct {
11 + reader io.ReadSeeker
12 +
13 + size int64
14 + offset int64
15 + realOffset int64
16 +}
17 +
18 +func (s *lazySeeker) Seek(offset int64, whence int) (int64, error) {
19 + switch whence {
20 + case io.SeekEnd:
21 + return s.Seek(s.size+offset, io.SeekStart)
22 + case io.SeekCurrent:
23 + return s.Seek(s.offset+offset, io.SeekStart)
24 + case io.SeekStart:
25 + if offset < 0 {
26 + return s.offset, fmt.Errorf("invalid seek offset")
27 + }
28 + s.offset = offset
29 + return s.offset, nil
30 + default:
31 + return s.offset, fmt.Errorf("invalid whence: %d", whence)
32 + }
33 +}
34 +
35 +func (s *lazySeeker) Read(b []byte) (int, error) {
36 + // If we're past the end, EOF.
37 + if s.offset >= s.size {
38 + return 0, io.EOF
39 + }
40 +
41 + // actually seek
42 + for s.offset != s.realOffset {
43 + off, err := s.reader.Seek(s.offset, io.SeekStart)
44 + if err != nil {
45 + return 9, err
46 + }
47 + s.realOffset = off
48 + }
49 + off, err := s.reader.Read(b)
50 + s.realOffset += int64(off)
51 + s.offset += int64(off)
52 + return off, err
53 +}
54 +
55 +func (s *lazySeeker) Close() error {
56 + if closer, ok := s.reader.(io.Closer); ok {
57 + return closer.Close()
58 + }
59 + return nil
60 +}