github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/mime/multipart/formdata_test.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package multipart 6 7 import ( 8 "io" 9 "math" 10 "os" 11 "strings" 12 "testing" 13 ) 14 15 func TestReadForm(t *testing.T) { 16 b := strings.NewReader(strings.ReplaceAll(message, "\n", "\r\n")) 17 r := NewReader(b, boundary) 18 f, err := r.ReadForm(25) 19 if err != nil { 20 t.Fatal("ReadForm:", err) 21 } 22 defer f.RemoveAll() 23 if g, e := f.Value["texta"][0], textaValue; g != e { 24 t.Errorf("texta value = %q, want %q", g, e) 25 } 26 if g, e := f.Value["textb"][0], textbValue; g != e { 27 t.Errorf("texta value = %q, want %q", g, e) 28 } 29 fd := testFile(t, f.File["filea"][0], "filea.txt", fileaContents) 30 if _, ok := fd.(*os.File); ok { 31 t.Error("file is *os.File, should not be") 32 } 33 fd.Close() 34 fd = testFile(t, f.File["fileb"][0], "fileb.txt", filebContents) 35 if _, ok := fd.(*os.File); !ok { 36 t.Errorf("file has unexpected underlying type %T", fd) 37 } 38 fd.Close() 39 } 40 41 func TestReadFormWithNamelessFile(t *testing.T) { 42 b := strings.NewReader(strings.ReplaceAll(messageWithFileWithoutName, "\n", "\r\n")) 43 r := NewReader(b, boundary) 44 f, err := r.ReadForm(25) 45 if err != nil { 46 t.Fatal("ReadForm:", err) 47 } 48 defer f.RemoveAll() 49 50 if g, e := f.Value["hiddenfile"][0], filebContents; g != e { 51 t.Errorf("hiddenfile value = %q, want %q", g, e) 52 } 53 } 54 55 // Issue 40430: Handle ReadForm(math.MaxInt64) 56 func TestReadFormMaxMemoryOverflow(t *testing.T) { 57 b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n")) 58 r := NewReader(b, boundary) 59 f, err := r.ReadForm(math.MaxInt64) 60 if err != nil { 61 t.Fatalf("ReadForm(MaxInt64): %v", err) 62 } 63 if f == nil { 64 t.Fatal("ReadForm(MaxInt64): missing form") 65 } 66 } 67 68 func TestReadFormWithTextContentType(t *testing.T) { 69 // From https://github.com/golang/go/issues/24041 70 b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n")) 71 r := NewReader(b, boundary) 72 f, err := r.ReadForm(25) 73 if err != nil { 74 t.Fatal("ReadForm:", err) 75 } 76 defer f.RemoveAll() 77 78 if g, e := f.Value["texta"][0], textaValue; g != e { 79 t.Errorf("texta value = %q, want %q", g, e) 80 } 81 } 82 83 func testFile(t *testing.T, fh *FileHeader, efn, econtent string) File { 84 if fh.Filename != efn { 85 t.Errorf("filename = %q, want %q", fh.Filename, efn) 86 } 87 if fh.Size != int64(len(econtent)) { 88 t.Errorf("size = %d, want %d", fh.Size, len(econtent)) 89 } 90 f, err := fh.Open() 91 if err != nil { 92 t.Fatal("opening file:", err) 93 } 94 b := new(strings.Builder) 95 _, err = io.Copy(b, f) 96 if err != nil { 97 t.Fatal("copying contents:", err) 98 } 99 if g := b.String(); g != econtent { 100 t.Errorf("contents = %q, want %q", g, econtent) 101 } 102 return f 103 } 104 105 const ( 106 fileaContents = "This is a test file." 107 filebContents = "Another test file." 108 textaValue = "foo" 109 textbValue = "bar" 110 boundary = `MyBoundary` 111 ) 112 113 const messageWithFileWithoutName = ` 114 --MyBoundary 115 Content-Disposition: form-data; name="hiddenfile"; filename="" 116 Content-Type: text/plain 117 118 ` + filebContents + ` 119 --MyBoundary-- 120 ` 121 122 const messageWithTextContentType = ` 123 --MyBoundary 124 Content-Disposition: form-data; name="texta" 125 Content-Type: text/plain 126 127 ` + textaValue + ` 128 --MyBoundary 129 ` 130 131 const message = ` 132 --MyBoundary 133 Content-Disposition: form-data; name="filea"; filename="filea.txt" 134 Content-Type: text/plain 135 136 ` + fileaContents + ` 137 --MyBoundary 138 Content-Disposition: form-data; name="fileb"; filename="fileb.txt" 139 Content-Type: text/plain 140 141 ` + filebContents + ` 142 --MyBoundary 143 Content-Disposition: form-data; name="texta" 144 145 ` + textaValue + ` 146 --MyBoundary 147 Content-Disposition: form-data; name="textb" 148 149 ` + textbValue + ` 150 --MyBoundary-- 151 ` 152 153 func TestReadForm_NoReadAfterEOF(t *testing.T) { 154 maxMemory := int64(32) << 20 155 boundary := `---------------------------8d345eef0d38dc9` 156 body := ` 157 -----------------------------8d345eef0d38dc9 158 Content-Disposition: form-data; name="version" 159 160 171 161 -----------------------------8d345eef0d38dc9--` 162 163 mr := NewReader(&failOnReadAfterErrorReader{t: t, r: strings.NewReader(body)}, boundary) 164 165 f, err := mr.ReadForm(maxMemory) 166 if err != nil { 167 t.Fatal(err) 168 } 169 t.Logf("Got: %#v", f) 170 } 171 172 // failOnReadAfterErrorReader is an io.Reader wrapping r. 173 // It fails t if any Read is called after a failing Read. 174 type failOnReadAfterErrorReader struct { 175 t *testing.T 176 r io.Reader 177 sawErr error 178 } 179 180 func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) { 181 if r.sawErr != nil { 182 r.t.Fatalf("unexpected Read on Reader after previous read saw error %v", r.sawErr) 183 } 184 n, err = r.r.Read(p) 185 r.sawErr = err 186 return 187 } 188 189 // TestReadForm_NonFileMaxMemory asserts that the ReadForm maxMemory limit is applied 190 // while processing non-file form data as well as file form data. 191 func TestReadForm_NonFileMaxMemory(t *testing.T) { 192 n := 10<<20 + 25 193 if testing.Short() { 194 n = 10<<10 + 25 195 } 196 largeTextValue := strings.Repeat("1", n) 197 message := `--MyBoundary 198 Content-Disposition: form-data; name="largetext" 199 200 ` + largeTextValue + ` 201 --MyBoundary-- 202 ` 203 204 testBody := strings.ReplaceAll(message, "\n", "\r\n") 205 testCases := []struct { 206 name string 207 maxMemory int64 208 err error 209 }{ 210 {"smaller", 50, nil}, 211 {"exact-fit", 25, nil}, 212 {"too-large", 0, ErrMessageTooLarge}, 213 } 214 for _, tc := range testCases { 215 t.Run(tc.name, func(t *testing.T) { 216 if tc.maxMemory == 0 && testing.Short() { 217 t.Skip("skipping in -short mode") 218 } 219 b := strings.NewReader(testBody) 220 r := NewReader(b, boundary) 221 f, err := r.ReadForm(tc.maxMemory) 222 if err == nil { 223 defer f.RemoveAll() 224 } 225 if tc.err != err { 226 t.Fatalf("ReadForm error - got: %v; expected: %v", tc.err, err) 227 } 228 if err == nil { 229 if g := f.Value["largetext"][0]; g != largeTextValue { 230 t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue)) 231 } 232 } 233 }) 234 } 235 }