Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
Take care of remaining TODOs in the browse responder
Matthew Holt committed
May 21, 2019 at 13:03 UTC
9d54f655aa7698ce683786dfc31b6f11df3c89d2
5 files changed
+31
-41
modules/caddyhttp/fileserver/browse.go
+24
-32
@@ -21,6 +21,16 @@ type Browse struct {
21
}
22
23
func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *http.Request) error {
24
+ // navigation on the client-side gets messed up if the
25
+ // URL doesn't end in a trailing slash because hrefs like
26
+ // "/b/c" on a path like "/a" end up going to "/b/c" instead
27
+ // of "/a/b/c" - so we have to redirect in this case
28
+ if !strings.HasSuffix(r.URL.Path, "/") {
29
+ r.URL.Path += "/"
30
+ http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
31
+ return nil
32
+ }
33
+
34
dir, err := fsrv.openFile(dirPath, w)
35
if err != nil {
36
return err
@@ -29,7 +39,8 @@ func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *ht
39
40
repl := r.Context().Value(caddy2.ReplacerCtxKey).(caddy2.Replacer)
41
32
- listing, err := fsrv.loadDirectoryContents(dir, r.URL.Path, repl)
42
+ // calling path.Clean here prevents weird breadcrumbs when URL paths are sketchy like /%2e%2e%2f
43
+ listing, err := fsrv.loadDirectoryContents(dir, path.Clean(r.URL.Path), repl)
44
switch {
45
case os.IsPermission(err):
46
return caddyhttp.Error(http.StatusForbidden, err)
@@ -58,21 +69,6 @@ func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *ht
69
buf.WriteTo(w)
70
71
return nil
61
-
62
- // TODO: Sigh... do we have to put this here?
63
- // // Browsing navigation gets messed up if browsing a directory
64
- // // that doesn't end in "/" (which it should, anyway)
65
- // u := *r.URL
66
- // if u.Path == "" {
67
- // u.Path = "/"
68
- // }
69
- // if u.Path[len(u.Path)-1] != '/' {
70
- // u.Path += "/"
71
- // http.Redirect(w, r, u.String(), http.StatusMovedPermanently)
72
- // return http.StatusMovedPermanently, nil
73
- // }
74
-
75
- // return b.ServeListing(w, r, requestedFilepath, bc)
72
}
73
74
func (fsrv *FileServer) loadDirectoryContents(dir *os.File, urlPath string, repl caddy2.Replacer) (browseListing, error) {
@@ -138,20 +134,16 @@ func isSymlink(f os.FileInfo) bool {
134
return f.Mode()&os.ModeSymlink != 0
135
}
136
141
-// isSymlinkTargetDir return true if f's symbolic link target
142
-// is a directory. Return false if not a symbolic link.
143
-// TODO: Re-implement
144
-func isSymlinkTargetDir(f os.FileInfo, urlPath string) bool {
145
- // if !isSymlink(f) {
146
- // return false
147
- // }
148
-
149
- // // TODO: Ensure path is sanitized
150
- // target:= path.Join(root, urlPath, f.Name()))
151
- // targetInfo, err := os.Stat(target)
152
- // if err != nil {
153
- // return false
154
- // }
155
- // return targetInfo.IsDir()
156
- return false
137
+// isSymlinkTargetDir returns true if f's symbolic link target
138
+// is a directory.
139
+func isSymlinkTargetDir(f os.FileInfo, root, urlPath string) bool {
140
+ if !isSymlink(f) {
141
+ return false
142
+ }
143
+ target := sanitizedPathJoin(root, path.Join(urlPath, f.Name()))
144
+ targetInfo, err := os.Stat(target)
145
+ if err != nil {
146
+ return false
147
+ }
148
+ return targetInfo.IsDir()
149
}
modules/caddyhttp/fileserver/browselisting.go
+1
-1
@@ -28,7 +28,7 @@ func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, urlP
28
continue
29
}
30
31
- isDir := f.IsDir() || isSymlinkTargetDir(f, urlPath)
31
+ isDir := f.IsDir() || isSymlinkTargetDir(f, fsrv.Root, urlPath)
32
33
if isDir {
34
name += "/"
modules/caddyhttp/fileserver/matcher.go
+1
-3
@@ -3,7 +3,6 @@ package fileserver
3
import (
4
"net/http"
5
"os"
6
- "path/filepath"
6
7
"bitbucket.org/lightcodelabs/caddy2"
8
"bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
@@ -33,8 +32,7 @@ type FileMatcher struct {
32
33
// Match matches the request r against m.
34
func (m FileMatcher) Match(r *http.Request) bool {
36
- // TODO: sanitize path
37
- fullPath := filepath.Join(m.Root, m.Path)
35
+ fullPath := sanitizedPathJoin(m.Root, m.Path)
36
var match bool
37
if len(m.Flags) > 0 {
38
match = true
modules/caddyhttp/fileserver/staticfiles.go
+2
-2
@@ -30,9 +30,9 @@ type FileServer struct {
30
Root string `json:"root"` // default is current directory
31
Hide []string `json:"hide"`
32
IndexNames []string `json:"index_names"`
33
- Files []string `json:"files"` // all relative to the root; default is request URI path
34
- Rehandle bool `json:"rehandle"` // issue a rehandle (internal redirect) if request is rewritten
33
+ Files []string `json:"files"` // all relative to the root; default is request URI path
34
SelectionPolicy string `json:"selection_policy"`
35
+ Rehandle bool `json:"rehandle"` // issue a rehandle (internal redirect) if request is rewritten
36
Fallback caddyhttp.RouteList `json:"fallback"`
37
Browse *Browse `json:"browse"`
38
// TODO: Etag
modules/caddyhttp/matchers.go
+3
-3
@@ -26,7 +26,7 @@ type (
26
matchHeaderRE map[string]*matchRegexp
27
matchProtocol string
28
matchStarlarkExpr string
29
- matchTable string
29
+ matchTable string // TODO: finish implementing
30
)
31
32
func init() {
@@ -245,14 +245,14 @@ func (mre *matchRegexp) match(input string, repl caddy2.Replacer, scope string)
245
246
// save all capture groups, first by index
247
for i, match := range matches {
248
- key := fmt.Sprintf("matchers.%s.%s.%d", scope, mre.Name, i)
248
+ key := fmt.Sprintf("http.matchers.%s.%s.%d", scope, mre.Name, i)
249
repl.Set(key, match)
250
}
251
252
// then by name
253
for i, name := range mre.compiled.SubexpNames() {
254
if i != 0 && name != "" {
255
- key := fmt.Sprintf("matchers.%s.%s.%s", scope, mre.Name, name)
255
+ key := fmt.Sprintf("http.matchers.%s.%s.%s", scope, mre.Name, name)
256
repl.Set(key, matches[i])
257
}
258
}