mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-01 11:52:47 +00:00
216 lines
5.2 KiB
Go
216 lines
5.2 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package multipart
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestReadForm(t *testing.T) {
|
|
b := strings.NewReader(strings.ReplaceAll(message, "\n", "\r\n"))
|
|
r := NewReader(b, boundary)
|
|
f, err := r.ReadForm(25)
|
|
if err != nil {
|
|
t.Fatal("ReadForm:", err)
|
|
}
|
|
defer f.RemoveAll()
|
|
if g, e := f.Value["texta"][0], textaValue; g != e {
|
|
t.Errorf("texta value = %q, want %q", g, e)
|
|
}
|
|
if g, e := f.Value["textb"][0], textbValue; g != e {
|
|
t.Errorf("texta value = %q, want %q", g, e)
|
|
}
|
|
fd := testFile(t, f.File["filea"][0], "filea.txt", fileaContents)
|
|
if _, ok := fd.(*os.File); ok {
|
|
t.Error("file is *os.File, should not be")
|
|
}
|
|
fd.Close()
|
|
fd = testFile(t, f.File["fileb"][0], "fileb.txt", filebContents)
|
|
if _, ok := fd.(*os.File); !ok {
|
|
t.Errorf("file has unexpected underlying type %T", fd)
|
|
}
|
|
fd.Close()
|
|
}
|
|
|
|
func TestReadFormWithNamelessFile(t *testing.T) {
|
|
b := strings.NewReader(strings.ReplaceAll(messageWithFileWithoutName, "\n", "\r\n"))
|
|
r := NewReader(b, boundary)
|
|
f, err := r.ReadForm(25)
|
|
if err != nil {
|
|
t.Fatal("ReadForm:", err)
|
|
}
|
|
defer f.RemoveAll()
|
|
|
|
if g, e := f.Value["hiddenfile"][0], filebContents; g != e {
|
|
t.Errorf("hiddenfile value = %q, want %q", g, e)
|
|
}
|
|
}
|
|
|
|
func TestReadFormWithTextContentType(t *testing.T) {
|
|
// From https://github.com/golang/go/issues/24041
|
|
b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
|
|
r := NewReader(b, boundary)
|
|
f, err := r.ReadForm(25)
|
|
if err != nil {
|
|
t.Fatal("ReadForm:", err)
|
|
}
|
|
defer f.RemoveAll()
|
|
|
|
if g, e := f.Value["texta"][0], textaValue; g != e {
|
|
t.Errorf("texta value = %q, want %q", g, e)
|
|
}
|
|
}
|
|
|
|
func testFile(t *testing.T, fh *FileHeader, efn, econtent string) File {
|
|
if fh.Filename != efn {
|
|
t.Errorf("filename = %q, want %q", fh.Filename, efn)
|
|
}
|
|
if fh.Size != int64(len(econtent)) {
|
|
t.Errorf("size = %d, want %d", fh.Size, len(econtent))
|
|
}
|
|
f, err := fh.Open()
|
|
if err != nil {
|
|
t.Fatal("opening file:", err)
|
|
}
|
|
b := new(bytes.Buffer)
|
|
_, err = io.Copy(b, f)
|
|
if err != nil {
|
|
t.Fatal("copying contents:", err)
|
|
}
|
|
if g := b.String(); g != econtent {
|
|
t.Errorf("contents = %q, want %q", g, econtent)
|
|
}
|
|
return f
|
|
}
|
|
|
|
const (
|
|
fileaContents = "This is a test file."
|
|
filebContents = "Another test file."
|
|
textaValue = "foo"
|
|
textbValue = "bar"
|
|
boundary = `MyBoundary`
|
|
)
|
|
|
|
const messageWithFileWithoutName = `
|
|
--MyBoundary
|
|
Content-Disposition: form-data; name="hiddenfile"; filename=""
|
|
Content-Type: text/plain
|
|
|
|
` + filebContents + `
|
|
--MyBoundary--
|
|
`
|
|
|
|
const messageWithTextContentType = `
|
|
--MyBoundary
|
|
Content-Disposition: form-data; name="texta"
|
|
Content-Type: text/plain
|
|
|
|
` + textaValue + `
|
|
--MyBoundary
|
|
`
|
|
|
|
const message = `
|
|
--MyBoundary
|
|
Content-Disposition: form-data; name="filea"; filename="filea.txt"
|
|
Content-Type: text/plain
|
|
|
|
` + fileaContents + `
|
|
--MyBoundary
|
|
Content-Disposition: form-data; name="fileb"; filename="fileb.txt"
|
|
Content-Type: text/plain
|
|
|
|
` + filebContents + `
|
|
--MyBoundary
|
|
Content-Disposition: form-data; name="texta"
|
|
|
|
` + textaValue + `
|
|
--MyBoundary
|
|
Content-Disposition: form-data; name="textb"
|
|
|
|
` + textbValue + `
|
|
--MyBoundary--
|
|
`
|
|
|
|
func TestReadForm_NoReadAfterEOF(t *testing.T) {
|
|
maxMemory := int64(32) << 20
|
|
boundary := `---------------------------8d345eef0d38dc9`
|
|
body := `
|
|
-----------------------------8d345eef0d38dc9
|
|
Content-Disposition: form-data; name="version"
|
|
|
|
171
|
|
-----------------------------8d345eef0d38dc9--`
|
|
|
|
mr := NewReader(&failOnReadAfterErrorReader{t: t, r: strings.NewReader(body)}, boundary)
|
|
|
|
f, err := mr.ReadForm(maxMemory)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Logf("Got: %#v", f)
|
|
}
|
|
|
|
// failOnReadAfterErrorReader is an io.Reader wrapping r.
|
|
// It fails t if any Read is called after a failing Read.
|
|
type failOnReadAfterErrorReader struct {
|
|
t *testing.T
|
|
r io.Reader
|
|
sawErr error
|
|
}
|
|
|
|
func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) {
|
|
if r.sawErr != nil {
|
|
r.t.Fatalf("unexpected Read on Reader after previous read saw error %v", r.sawErr)
|
|
}
|
|
n, err = r.r.Read(p)
|
|
r.sawErr = err
|
|
return
|
|
}
|
|
|
|
// TestReadForm_NonFileMaxMemory asserts that the ReadForm maxMemory limit is applied
|
|
// while processing non-file form data as well as file form data.
|
|
func TestReadForm_NonFileMaxMemory(t *testing.T) {
|
|
largeTextValue := strings.Repeat("1", (10<<20)+25)
|
|
message := `--MyBoundary
|
|
Content-Disposition: form-data; name="largetext"
|
|
|
|
` + largeTextValue + `
|
|
--MyBoundary--
|
|
`
|
|
|
|
testBody := strings.ReplaceAll(message, "\n", "\r\n")
|
|
testCases := []struct {
|
|
name string
|
|
maxMemory int64
|
|
err error
|
|
}{
|
|
{"smaller", 50, nil},
|
|
{"exact-fit", 25, nil},
|
|
{"too-large", 0, ErrMessageTooLarge},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
b := strings.NewReader(testBody)
|
|
r := NewReader(b, boundary)
|
|
f, err := r.ReadForm(tc.maxMemory)
|
|
if err == nil {
|
|
defer f.RemoveAll()
|
|
}
|
|
if tc.err != err {
|
|
t.Fatalf("ReadForm error - got: %v; expected: %v", tc.err, err)
|
|
}
|
|
if err == nil {
|
|
if g := f.Value["largetext"][0]; g != largeTextValue {
|
|
t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|