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