|  | // 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.Replace(message, "\n", "\r\n", -1)) | 
|  | 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.Replace(messageWithFileWithoutName, "\n", "\r\n", -1)) | 
|  | r := NewReader(b, boundary) | 
|  | f, err := r.ReadForm(25) | 
|  | if err != nil { | 
|  | t.Fatal("ReadForm:", err) | 
|  | } | 
|  | defer f.RemoveAll() | 
|  |  | 
|  | fd := testFile(t, f.File["hiddenfile"][0], "", filebContents) | 
|  | if _, ok := fd.(sectionReadCloser); !ok { | 
|  | t.Errorf("file has unexpected underlying type %T", fd) | 
|  | } | 
|  | fd.Close() | 
|  |  | 
|  | } | 
|  |  | 
|  | 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 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.Replace(message, "\n", "\r\n", -1) | 
|  | 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)) | 
|  | } | 
|  | } | 
|  | }) | 
|  | } | 
|  | } |