forked from gravitational/teleport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathassets.go
103 lines (89 loc) · 2.31 KB
/
assets.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
/*
Copyright 2021 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package web
import (
"archive/zip"
"io"
"net/http"
"os"
"strings"
"github.com/gravitational/trace"
)
// resource struct implements http.File interface on top of zip.File object
type resource struct {
reader io.ReadCloser
file *zip.File
pos int64
}
func (rsc *resource) Read(p []byte) (n int, err error) {
n, err = rsc.reader.Read(p)
rsc.pos += int64(n)
return n, err
}
func (rsc *resource) Seek(offset int64, whence int) (int64, error) {
var (
pos int64
err error
)
// zip.File does not support seeking. To implement Seek on top of it,
// we close the existing reader, re-open it, and read 'offset' bytes from
// the beginning
if err = rsc.reader.Close(); err != nil {
return 0, err
}
if rsc.reader, err = rsc.file.Open(); err != nil {
return 0, err
}
switch whence {
case io.SeekStart:
pos = offset
case io.SeekCurrent:
pos = rsc.pos + offset
case io.SeekEnd:
pos = int64(rsc.file.UncompressedSize64) + offset
}
if pos > 0 {
b := make([]byte, pos)
if _, err = rsc.reader.Read(b); err != nil {
return 0, err
}
}
rsc.pos = pos
return pos, nil
}
func (rsc *resource) Readdir(count int) ([]os.FileInfo, error) {
return nil, trace.Wrap(os.ErrPermission)
}
func (rsc *resource) Stat() (os.FileInfo, error) {
return rsc.file.FileInfo(), nil
}
func (rsc *resource) Close() (err error) {
log.Debugf("zip::Close(%s).", rsc.file.FileInfo().Name())
return rsc.reader.Close()
}
type ResourceMap map[string]*zip.File
func (rm ResourceMap) Open(name string) (http.File, error) {
log.Debugf("GET zip:%s.", name)
f, ok := rm[strings.Trim(name, "/")]
if !ok {
return nil, trace.Wrap(os.ErrNotExist)
}
reader, err := f.Open()
if err != nil {
return nil, trace.Wrap(err)
}
return &resource{
reader: reader,
file: f,
}, nil
}