@@ -7,6 +7,7 @@ package public
77import (
88 "net/http"
99 "os"
10+ "path"
1011 "path/filepath"
1112 "strings"
1213
@@ -17,12 +18,13 @@ import (
1718
1819// Options represents the available options to configure the handler.
1920type Options struct {
20- Directory string
21- Prefix string
21+ Directory string
22+ Prefix string
23+ CorsHandler func(http.Handler) http.Handler
2224}
2325
2426// AssetsHandler implements the static handler for serving custom or original assets.
25- func AssetsHandler(opts *Options) func(resp http.ResponseWriter, req * http.Request) {
27+ func AssetsHandler(opts *Options) func(next http.Handler) http.Handler {
2628 var custPath = filepath.Join(setting.CustomPath, "public")
2729 if !filepath.IsAbs(custPath) {
2830 custPath = filepath.Join(setting.AppWorkPath, custPath)
@@ -31,19 +33,55 @@ func AssetsHandler(opts *Options) func(resp http.ResponseWriter, req *http.Reque
3133 if !filepath.IsAbs(opts.Directory) {
3234 opts.Directory = filepath.Join(setting.AppWorkPath, opts.Directory)
3335 }
36+ if opts.Prefix == "" {
37+ opts.Prefix = "/"
38+ }
3439
35- return func(resp http.ResponseWriter, req *http.Request) {
36- // custom files
37- if opts.handle(resp, req, http.Dir(custPath), opts.Prefix) {
38- return
39- }
40-
41- // internal files
42- if opts.handle(resp, req, fileSystem(opts.Directory), opts.Prefix) {
43- return
44- }
45-
46- resp.WriteHeader(404)
40+ return func(next http.Handler) http.Handler {
41+ return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
42+ if !strings.HasPrefix(req.URL.Path, opts.Prefix) {
43+ next.ServeHTTP(resp, req)
44+ return
45+ }
46+ if req.Method != "GET" && req.Method != "HEAD" {
47+ resp.WriteHeader(http.StatusNotFound)
48+ return
49+ }
50+
51+ file := req.URL.Path
52+ file = file[len(opts.Prefix):]
53+ if len(file) == 0 {
54+ resp.WriteHeader(http.StatusNotFound)
55+ return
56+ }
57+ if !strings.HasPrefix(file, "/") {
58+ next.ServeHTTP(resp, req)
59+ return
60+ }
61+
62+ var written bool
63+ if opts.CorsHandler != nil {
64+ written = true
65+ opts.CorsHandler(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
66+ written = false
67+ })).ServeHTTP(resp, req)
68+ }
69+ if written {
70+ return
71+ }
72+
73+ // custom files
74+ if opts.handle(resp, req, http.Dir(custPath), file) {
75+ return
76+ }
77+
78+ // internal files
79+ if opts.handle(resp, req, fileSystem(opts.Directory), file) {
80+ return
81+ }
82+
83+ resp.WriteHeader(http.StatusNotFound)
84+ })
4785 }
4886}
4987
@@ -57,44 +95,29 @@ func parseAcceptEncoding(val string) map[string]bool {
5795 return types
5896}
5997
60- func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.FileSystem, prefix string) bool {
61- if req.Method != "GET" && req.Method != "HEAD" {
62- return false
63- }
64-
65- file := req.URL.Path
66- // if we have a prefix, filter requests by stripping the prefix
67- if prefix != "" {
68- if !strings.HasPrefix(file, prefix) {
69- return false
70- }
71- file = file[len(prefix):]
72- if file != "" && file[0] != '/' {
73- return false
74- }
75- }
76-
77- f, err := fs.Open(file)
98+ func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) bool {
99+ // use clean to keep the file is a valid path with no . or ..
100+ f, err := fs.Open(path.Clean(file))
78101 if err != nil {
79102 if os.IsNotExist(err) {
80103 return false
81104 }
82- w.WriteHeader(500 )
105+ w.WriteHeader(http.StatusInternalServerError )
83106 log.Error("[Static] Open %q failed: %v", file, err)
84107 return true
85108 }
86109 defer f.Close()
87110
88111 fi, err := f.Stat()
89112 if err != nil {
90- w.WriteHeader(500 )
113+ w.WriteHeader(http.StatusInternalServerError )
91114 log.Error("[Static] %q exists, but fails to open: %v", file, err)
92115 return true
93116 }
94117
95118 // Try to serve index file
96119 if fi.IsDir() {
97- w.WriteHeader(404 )
120+ w.WriteHeader(http.StatusNotFound )
98121 return true
99122 }
100123
0 commit comments