forked from gocraft/web
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstatic_middleware.go
131 lines (115 loc) · 3 KB
/
static_middleware.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
package web
import (
"log"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/gocraft/web"
)
var Root string
// StaticOptions is a struct for specifying configuration options for the martini.Static middleware.
type StaticOptions struct {
// Prefix is the optional prefix used to serve the static directory content
Prefix string
// SkipLogging will disable [Static] log messages when a static file is served.
SkipLogging bool
// IndexFile defines which file to serve as index if it exists.
IndexFile string
// Expires defines which user-defined function to use for producing a HTTP Expires Header
// https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
Expires func() string
}
func init() {
var err error
Root, err = os.Getwd()
if err != nil {
panic(err)
}
}
func prepareStaticOptions(options []StaticOptions) StaticOptions {
var opt StaticOptions
if len(options) > 0 {
opt = options[0]
}
// Defaults
if len(opt.IndexFile) == 0 {
opt.IndexFile = "index.html"
}
// Normalize the prefix if provided
if opt.Prefix != "" {
// Ensure we have a leading '/'
if opt.Prefix[0] != '/' {
opt.Prefix = "/" + opt.Prefix
}
// Remove any trailing '/'
opt.Prefix = strings.TrimRight(opt.Prefix, "/")
}
return opt
}
// Static returns a middleware handler that serves static files in the given directory.
func Static(directory string, staticOpt ...StaticOptions) func(web.ResponseWriter, *web.Request, web.NextMiddlewareFunc) {
if !filepath.IsAbs(directory) {
directory = filepath.Join(Root, directory)
}
dir := http.Dir(directory)
opt := prepareStaticOptions(staticOpt)
return func(w web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) {
if req.Method != "GET" && req.Method != "HEAD" {
next(w, req)
return
}
file := req.URL.Path
// if we have a prefix, filter requests by stripping the prefix
if opt.Prefix != "" {
if !strings.HasPrefix(file, opt.Prefix) {
next(w, req)
return
}
file = file[len(opt.Prefix):]
if file != "" && file[0] != '/' {
next(w, req)
return
}
}
f, err := dir.Open(file)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
// try to serve index file
if fi.IsDir() {
// redirect if missing trailing slash
if !strings.HasSuffix(req.URL.Path, "/") {
http.Redirect(w, req.Request, req.URL.Path+"/", http.StatusFound)
return
}
file = filepath.Join(file, opt.IndexFile)
f, err = dir.Open(file)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
defer f.Close()
fi, err = f.Stat()
if err != nil || fi.IsDir() {
w.WriteHeader(http.StatusNotFound)
return
}
}
if !opt.SkipLogging {
log.Println("[Static] Serving " + file)
}
// Add an Expires header to the static content
if opt.Expires != nil {
w.Header().Set("Expires", opt.Expires())
}
http.ServeContent(w, req.Request, file, fi.ModTime(), f)
}
}