85 lines
2.6 KiB
Go
85 lines
2.6 KiB
Go
package server
|
|
|
|
import (
|
|
"mime"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Content-Disposition is the header that keeps an uploaded file from being
|
|
// rendered, so it has to survive whatever a filename throws at it.
|
|
func TestContentDisposition(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
wantASCII string
|
|
wantExt bool // expect an RFC 5987 filename* parameter too
|
|
}{
|
|
{"MyGame.zip", "MyGame.zip", false},
|
|
{"notes (draft).txt", "notes (draft).txt", false},
|
|
{`quo"te.txt`, "quo_te.txt", true},
|
|
{`back\slash.txt`, "back_slash.txt", true},
|
|
{"naïve.txt", "na_ve.txt", true},
|
|
{"日本語.bin", "___.bin", true},
|
|
{"🙂.png", "_.png", true},
|
|
}
|
|
for _, c := range cases {
|
|
got := contentDisposition(c.name)
|
|
|
|
// It must always parse, and always be an attachment.
|
|
disp, params, err := mime.ParseMediaType(got)
|
|
if err != nil {
|
|
t.Errorf("contentDisposition(%q) = %q: does not parse: %v", c.name, got, err)
|
|
continue
|
|
}
|
|
if disp != "attachment" {
|
|
t.Errorf("contentDisposition(%q): disposition = %q", c.name, disp)
|
|
}
|
|
// Whatever the encoding, the decoded filename must be the real one when
|
|
// an extended parameter is present.
|
|
if c.wantExt {
|
|
if !strings.Contains(got, "filename*=UTF-8''") {
|
|
t.Errorf("contentDisposition(%q) = %q: no RFC 5987 parameter", c.name, got)
|
|
}
|
|
if params["filename"] != c.name {
|
|
t.Errorf("contentDisposition(%q): decoded filename = %q", c.name, params["filename"])
|
|
}
|
|
} else if params["filename"] != c.wantASCII {
|
|
t.Errorf("contentDisposition(%q): filename = %q, want %q", c.name, params["filename"], c.wantASCII)
|
|
}
|
|
|
|
// Nothing may break out of the header, in any spelling.
|
|
if strings.ContainsAny(got, "\r\n") {
|
|
t.Errorf("contentDisposition(%q) = %q: contains a line break", c.name, got)
|
|
}
|
|
if i := strings.Index(got, `filename="`); i >= 0 {
|
|
quoted := got[i+len(`filename="`):]
|
|
quoted = quoted[:strings.Index(quoted, `"`)]
|
|
if strings.ContainsAny(quoted, `"\`) {
|
|
t.Errorf("contentDisposition(%q): unescaped quoting in %q", c.name, quoted)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestASCIIFilenameNeverEmpty(t *testing.T) {
|
|
for _, in := range []string{"", "...", "___", " ", "🙂"} {
|
|
if got := asciiFilename(in); strings.Trim(got, "_. ") == "" {
|
|
t.Errorf("asciiFilename(%q) = %q, which names nothing", in, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEncodeRFC5987(t *testing.T) {
|
|
cases := map[string]string{
|
|
"plain.txt": "plain.txt",
|
|
"a b.txt": "a%20b.txt",
|
|
"naïve": "na%C3%AFve",
|
|
`q"x`: "q%22x",
|
|
}
|
|
for in, want := range cases {
|
|
if got := encodeRFC5987(in); got != want {
|
|
t.Errorf("encodeRFC5987(%q) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|