Skip to content

Commit 099479a

Browse files
authored
feat: add a config option to bypass mime content type sniffing (#60)
1 parent 8cd6d0a commit 099479a

File tree

3 files changed

+99
-8
lines changed

3 files changed

+99
-8
lines changed

cmd/config.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,11 @@ func parseUsers(raw []interface{}, c *lib.Config) {
122122
}
123123

124124
user.Handler = &webdav.Handler{
125-
Prefix: c.User.Handler.Prefix,
126-
FileSystem: webdav.Dir(user.Scope),
125+
Prefix: c.User.Handler.Prefix,
126+
FileSystem: lib.WebDavDir{
127+
Dir: webdav.Dir(user.Scope),
128+
NoSniff: c.NoSniff,
129+
},
127130
LockSystem: webdav.NewMemLS(),
128131
}
129132

@@ -179,12 +182,16 @@ func readConfig(flags *pflag.FlagSet) *lib.Config {
179182
Modify: getOptB(flags, "modify"),
180183
Rules: []*lib.Rule{},
181184
Handler: &webdav.Handler{
182-
Prefix: getOpt(flags, "prefix"),
183-
FileSystem: webdav.Dir(getOpt(flags, "scope")),
185+
Prefix: getOpt(flags, "prefix"),
186+
FileSystem: lib.WebDavDir{
187+
Dir: webdav.Dir(getOpt(flags, "scope")),
188+
NoSniff: getOptB(flags, "nosniff"),
189+
},
184190
LockSystem: webdav.NewMemLS(),
185191
},
186192
},
187-
Auth: getOptB(flags, "auth"),
193+
Auth: getOptB(flags, "auth"),
194+
NoSniff: getOptB(flags, "nosniff"),
188195
Cors: lib.CorsCfg{
189196
Enabled: false,
190197
Credentials: false,

lib/dir.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package lib
2+
3+
import (
4+
"context"
5+
"mime"
6+
"os"
7+
"path"
8+
9+
"golang.org/x/net/webdav"
10+
)
11+
12+
// NoSniffFileInfo wraps any generic FileInfo interface and bypasses mime type sniffing.
13+
type NoSniffFileInfo struct {
14+
os.FileInfo
15+
}
16+
17+
func (w NoSniffFileInfo) ContentType(ctx context.Context) (contentType string, err error) {
18+
if mimeType := mime.TypeByExtension(path.Ext(w.FileInfo.Name())); mimeType != "" {
19+
// We can figure out the mime from the extension.
20+
return mimeType, nil
21+
} else {
22+
// We can't figure out the mime type without sniffing, call it an octet stream.
23+
return "application/octet-stream", nil
24+
}
25+
}
26+
27+
type WebDavDir struct {
28+
webdav.Dir
29+
NoSniff bool
30+
}
31+
32+
func (d WebDavDir) Stat(ctx context.Context, name string) (os.FileInfo, error) {
33+
// Skip wrapping if NoSniff is off
34+
if !d.NoSniff {
35+
return d.Dir.Stat(ctx, name)
36+
}
37+
38+
info, err := d.Dir.Stat(ctx, name)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
return NoSniffFileInfo{info}, nil
44+
}
45+
46+
func (d WebDavDir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
47+
// Skip wrapping if NoSniff is off
48+
if !d.NoSniff {
49+
return d.Dir.OpenFile(ctx, name, flag, perm)
50+
}
51+
52+
file, err := d.Dir.OpenFile(ctx, name, flag, perm)
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
return WebDavFile{File: file}, nil
58+
}
59+
60+
type WebDavFile struct {
61+
webdav.File
62+
}
63+
64+
func (f WebDavFile) Stat() (os.FileInfo, error) {
65+
info, err := f.File.Stat()
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
return NoSniffFileInfo{info}, nil
71+
}
72+
73+
func (f WebDavFile) Readdir(count int) (fis []os.FileInfo, err error) {
74+
fis, err = f.File.Readdir(count)
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
for i := range fis {
80+
fis[i] = NoSniffFileInfo{fis[i]}
81+
}
82+
return fis, nil
83+
}

lib/webdav.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ type CorsCfg struct {
2020
// Config is the configuration of a WebDAV instance.
2121
type Config struct {
2222
*User
23-
Auth bool
24-
Cors CorsCfg
25-
Users map[string]*User
23+
Auth bool
24+
NoSniff bool
25+
Cors CorsCfg
26+
Users map[string]*User
2627
}
2728

2829
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.

0 commit comments

Comments
 (0)