/
httpfs.go
95 lines (81 loc) · 2.38 KB
/
httpfs.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
// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package v5 // import "gopkg.in/juju/charmstore.v5/internal/v5"
import (
"fmt"
"io"
"net/http"
"os"
"time"
)
// serveContent serves the given content as a single HTTP endpoint.
// We use http.FileServer under the covers because that
// provides us with all the HTTP Content-Range goodness
// that we'd like.
// TODO use http.ServeContent instead of this.
func serveContent(w http.ResponseWriter, req *http.Request, length int64, content io.ReadSeeker) {
fs := &archiveFS{
length: length,
ReadSeeker: content,
}
// Copy the request and mutate the path to pretend
// we're looking for the given file.
nreq := *req
nreq.URL.Path = "/archive.zip"
h := http.FileServer(fs)
h.ServeHTTP(w, &nreq)
}
// archiveFS implements http.FileSystem to serve a single file.
// http.FileSystem.Open returns an http.File; http.File.Stat returns an
// os.FileInfo. We implement methods for all of those interfaces on the
// same type, and return the same value for all the aforementioned
// methods, since we only ever need one instance of any of them.
type archiveFS struct {
length int64
io.ReadSeeker
}
// Open implements http.FileSystem.Open.
func (fs *archiveFS) Open(name string) (http.File, error) {
if name != "/archive.zip" {
return nil, fmt.Errorf("unexpected name %q", name)
}
return fs, nil
}
// Close implements http.File.Close.
// It does not actually close anything because
// that responsibility is left to the caller of serveContent.
func (fs *archiveFS) Close() error {
return nil
}
// Stat implements http.File.Stat.
func (fs *archiveFS) Stat() (os.FileInfo, error) {
return fs, nil
}
// Readdir implements http.File.Readdir.
func (fs *archiveFS) Readdir(count int) ([]os.FileInfo, error) {
return nil, fmt.Errorf("not a directory")
}
// Name implements os.FileInfo.Name.
func (fs *archiveFS) Name() string {
return "archive"
}
// Size implements os.FileInfo.Size.
func (fs *archiveFS) Size() int64 {
return fs.length
}
// Mode implements os.FileInfo.Mode.
func (fs *archiveFS) Mode() os.FileMode {
return 0444
}
// ModTime implements os.FileInfo.ModTime.
func (fs *archiveFS) ModTime() time.Time {
return time.Time{}
}
// IsDir implements os.FileInfo.IsDir.
func (fs *archiveFS) IsDir() bool {
return false
}
// Sys implements os.FileInfo.Sys.
func (fs *archiveFS) Sys() interface{} {
return nil
}