/
fsutil.go
207 lines (166 loc) · 6.58 KB
/
fsutil.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package xin
import (
"bytes"
"net/http"
"path"
"strings"
"time"
"github.com/askasoft/pango/net/httpx"
)
func ServeFileHandler(filePath string) HandlerFunc {
return func(c *Context) {
http.ServeFile(c.Writer, c.Request, filePath)
}
}
func ServeFSFileHandler(hfs http.FileSystem, filePath string) HandlerFunc {
return func(c *Context) {
org := c.Request.URL.Path
defer func(url string) {
c.Request.URL.Path = url
}(org)
c.Request.URL.Path = filePath
http.FileServer(hfs).ServeHTTP(c.Writer, c.Request)
}
}
func ServeFSFuncFileHandler(hfsc func(c *Context) http.FileSystem, filePath string) HandlerFunc {
return func(c *Context) {
org := c.Request.URL.Path
defer func(url string) {
c.Request.URL.Path = url
}(org)
c.Request.URL.Path = filePath
hfs := hfsc(c)
http.FileServer(hfs).ServeHTTP(c.Writer, c.Request)
}
}
func ServeFSHandler(prefix string, hfs http.FileSystem, filePath string) HandlerFunc {
fileServer := httpx.FileServer(prefix, hfs, filePath)
return func(c *Context) {
fileServer.ServeHTTP(c.Writer, c.Request)
}
}
func ServeFSFuncHandler(prefix string, hfsc func(c *Context) http.FileSystem, filePath string) HandlerFunc {
return func(c *Context) {
hfs := hfsc(c)
fileServer := httpx.FileServer(prefix, hfs, filePath)
fileServer.ServeHTTP(c.Writer, c.Request)
}
}
func ServeContentHandler(data []byte, modtime time.Time) HandlerFunc {
if modtime.IsZero() {
modtime = time.Now()
}
return func(c *Context) {
name := path.Base(c.Request.URL.Path)
http.ServeContent(c.Writer, c.Request, name, modtime, bytes.NewReader(data))
}
}
// StaticFile registers a single route in order to serve a single file of the local filesystem.
// example:
//
// xin.StaticFile(r, "favicon.ico", "./resources/favicon.ico", xin.NewCacheControlSetter("public, max-age=31536000").Handler())
func StaticFile(r IRoutes, relativePath, filePath string, handlers ...HandlerFunc) {
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
panic("URL parameters can not be used when serving a static file")
}
handler := ServeFileHandler(filePath)
handlers = append(handlers, handler)
r.GET(relativePath, handlers...)
r.HEAD(relativePath, handlers...)
}
// Static serves files from the given file system root.
// Internally a http.FileServer is used, therefore http.NotFound is used instead
// of the Router's NotFound handler.
// example:
//
// xin.Static(r, "/static", "/var/www", xin.NewCacheControlSetter("public, max-age=31536000").Handler())
func Static(r IRoutes, relativePath, root string, handlers ...HandlerFunc) {
StaticFS(r, relativePath, httpx.Dir(root), "", handlers...)
}
// StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead.
func StaticFS(r IRoutes, relativePath string, hfs http.FileSystem, filePath string, handlers ...HandlerFunc) {
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
panic("URL parameters can not be used when serving a static folder")
}
prefix := path.Join(r.BasePath(), relativePath)
handler := ServeFSHandler(prefix, hfs, filePath)
handlers = append(handlers, handler)
// Register GET and HEAD handlers
urlPattern := path.Join(relativePath, "/*path")
r.GET(urlPattern, handlers...)
r.HEAD(urlPattern, handlers...)
}
// StaticFSFunc works just like `StaticFS()` but a dynamic `http.FileSystem` can be used instead.
func StaticFSFunc(r IRoutes, relativePath string, hfsc func(c *Context) http.FileSystem, filePath string, handlers ...HandlerFunc) {
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
panic("URL parameters can not be used when serving a static folder")
}
prefix := path.Join(r.BasePath(), relativePath)
handler := ServeFSFuncHandler(prefix, hfsc, filePath)
handlers = append(handlers, handler)
// Register GET and HEAD handlers
urlPattern := path.Join(relativePath, "/*path")
r.GET(urlPattern, handlers...)
r.HEAD(urlPattern, handlers...)
}
// StaticFSFile registers a single route in order to serve a single file of the filesystem.
// xin.StaticFSFile(r, "favicon.ico", hfs, "./resources/favicon.ico", xin.NewCacheControlSetter("public, max-age=31536000").Handler())
func StaticFSFile(r IRoutes, relativePath string, hfs http.FileSystem, filePath string, handlers ...HandlerFunc) {
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
panic("URL parameters can not be used when serving a static file")
}
handler := ServeFSFileHandler(hfs, filePath)
handlers = append(handlers, handler)
r.GET(relativePath, handlers...)
r.HEAD(relativePath, handlers...)
}
// StaticFSFuncFile works just like `StaticFSFile()` but a dynamic `http.FileSystem` can be used instead.
func StaticFSFuncFile(r IRoutes, relativePath string, hfsc func(c *Context) http.FileSystem, filePath string, handlers ...HandlerFunc) {
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
panic("URL parameters can not be used when serving a static file")
}
handler := ServeFSFuncFileHandler(hfsc, filePath)
handlers = append(handlers, handler)
r.GET(relativePath, handlers...)
r.HEAD(relativePath, handlers...)
}
// StaticContent registers a single route in order to serve a single file of the data.
// example:
//
// //go:embed favicon.ico
// var favicon []byte
// xin.StaticContent(r, "favicon.ico", favicon, time.Now(), xin.NewCacheControlSetter("public, max-age=31536000").Handler())
func StaticContent(r IRoutes, relativePath string, data []byte, modtime time.Time, handlers ...HandlerFunc) {
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
panic("URL parameters can not be used when serving a static content")
}
handler := ServeContentHandler(data, modtime)
handlers = append(handlers, handler)
r.GET(relativePath, handlers...)
r.HEAD(relativePath, handlers...)
}
// -----------------------------------------------
// CacheControlSetter set Cache-Control header when statusCode == 200
type CacheControlSetter struct {
CacheControl string
}
func NewCacheControlSetter(cacheControls ...string) *CacheControlSetter {
ccs := &CacheControlSetter{}
ccs.SetCacheControl(cacheControls...)
return ccs
}
func (ccs *CacheControlSetter) SetCacheControl(cacheControls ...string) {
ccs.CacheControl = strings.Join(cacheControls, ", ")
}
func (ccs *CacheControlSetter) WrapWriter(w ResponseWriter) ResponseWriter {
if ccs.CacheControl == "" {
return w
}
return NewHeaderWriter(w, "Cache-Control", ccs.CacheControl)
}
func (ccs *CacheControlSetter) Handle(c *Context) {
c.Writer = ccs.WrapWriter(c.Writer)
}
func (ccs *CacheControlSetter) Handler() HandlerFunc {
return ccs.Handle
}