/
filesystem.go
164 lines (140 loc) · 4.25 KB
/
filesystem.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package store
import (
"bytes"
"io"
"io/ioutil"
"net/http"
"os"
"time"
"golang.org/x/net/context"
"golang.org/x/net/trace"
)
// FileSystem is an interface which defines an open method similar to http.FileSystem,
// but which also includes a context parameter.
type FileSystem interface {
Open(ctx context.Context, path string) (http.File, error)
}
// NewFileSystem creates a new FileSystem using an http.FileSystem as the underlying
// storage.
func NewFileSystem(fs http.FileSystem, name string) FileSystem {
return Trace(&fileSystem{fs}, name)
}
type fileSystem struct {
http.FileSystem
}
// Open implements FileSystem.
func (fs *fileSystem) Open(ctx context.Context, path string) (http.File, error) {
return fs.FileSystem.Open(path)
}
// Trace is a convenience method for adding a tracing wrapper around a FileSystem.
func Trace(fs FileSystem, name string) FileSystem {
return traceFileSystem{
fs: fs,
name: name,
}
}
// traceFileSystem is a type which wraps a FileSystem implementation. If a trace is associated
// to the context.Context passed to Open, then the trace will be updated to reflect the call.
type traceFileSystem struct {
fs FileSystem
name string
}
// Open implements FileSystem.
func (tfs traceFileSystem) Open(ctx context.Context, path string) (f http.File, err error) {
if tr, ok := trace.FromContext(ctx); ok {
tr.LazyPrintf("%v: open: %v", tfs.name, path)
defer func() {
if err != nil {
tr.LazyPrintf("%v: error: %v", tfs.name, err)
return
}
if stat, err := f.Stat(); err == nil {
tr.LazyPrintf("%v: got file: %v", tfs.name, stat.Name())
} else {
tr.LazyPrintf("%v: error in stat: %v", tfs.name, err)
}
}()
}
return tfs.fs.Open(ctx, path)
}
// RemoteFileSystem is an extension of the http.FileSystem interface
// which includes the RemoteOpen method.
type RemoteFileSystem interface {
FileSystem
// RemoteOpen returns a File which
RemoteOpen(context.Context, string) (*File, error)
}
// remoteFileSystem implements RemoteFileSystem
type remoteFileSystem struct {
client Client
}
// NewRemoteFileSystem creates a new file system using the given Client to handle
// file requests.
func NewRemoteFileSystem(c Client) RemoteFileSystem {
return &remoteFileSystem{
client: c,
}
}
// file is a basic representation of a remote file such that all operations (i.e.
// seeking) will work correctly.
type file struct {
io.ReadSeeker
stat *fileInfo
}
// RemoteOpen returns a *File which represents the remote file, and implements
// io.ReadCloser which reads the file contents from the remote system.
func (fs *remoteFileSystem) RemoteOpen(ctx context.Context, path string) (*File, error) {
rf, err := fs.client.Get(ctx, path)
if err != nil {
return nil, err
}
return rf, nil
}
// Open the given file and return an http.File implementation representing it. This method
// will block until the file has been completely fetched (http.File implements io.Seeker
// which means that for a trivial implementation we need all the underlying data).
func (fs *remoteFileSystem) Open(ctx context.Context, path string) (http.File, error) {
rf, err := fs.RemoteOpen(ctx, path)
if err != nil {
return nil, err
}
defer rf.Close()
buf, err := ioutil.ReadAll(rf)
if err != nil {
return nil, err
}
return &file{
ReadSeeker: bytes.NewReader(buf),
stat: &fileInfo{
name: rf.Name,
size: rf.Size,
modTime: rf.ModTime,
},
}, nil
}
// Close is a nop as we have already closed the original file.
func (f *file) Close() error {
return nil
}
// Implements http.File.
func (f *file) Readdir(int) ([]os.FileInfo, error) {
return nil, nil
}
// FileInfo is a simple implementation of os.FileInfo.
type fileInfo struct {
name string
size int64
modTime time.Time
}
func (f *fileInfo) Name() string { return f.name }
func (f *fileInfo) Size() int64 { return f.size }
func (f *fileInfo) Mode() os.FileMode { return os.FileMode(0777) }
func (f *fileInfo) ModTime() time.Time { return f.modTime }
func (f *fileInfo) IsDir() bool { return false }
func (f *fileInfo) Sys() interface{} { return nil }
func (f *file) Stat() (os.FileInfo, error) {
return f.stat, nil
}