1
2
3
4
5 package multipart
6
7 import (
8 "bytes"
9 "errors"
10 "internal/godebug"
11 "io"
12 "math"
13 "net/textproto"
14 "os"
15 "strconv"
16 )
17
18
19
20 var ErrMessageTooLarge = errors.New("multipart: message too large")
21
22
23
24
25
26
27
28
29
30
31
32 func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {
33 return r.readForm(maxMemory)
34 }
35
36 var (
37 multipartfiles = godebug.New("#multipartfiles")
38 multipartmaxparts = godebug.New("multipartmaxparts")
39 )
40
41 func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
42 form := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
43 var (
44 file *os.File
45 fileOff int64
46 )
47 numDiskFiles := 0
48 combineFiles := true
49 if multipartfiles.Value() == "distinct" {
50 combineFiles = false
51
52 }
53 maxParts := 1000
54 if s := multipartmaxparts.Value(); s != "" {
55 if v, err := strconv.Atoi(s); err == nil && v >= 0 {
56 maxParts = v
57 multipartmaxparts.IncNonDefault()
58 }
59 }
60 maxHeaders := maxMIMEHeaders()
61
62 defer func() {
63 if file != nil {
64 if cerr := file.Close(); err == nil {
65 err = cerr
66 }
67 }
68 if combineFiles && numDiskFiles > 1 {
69 for _, fhs := range form.File {
70 for _, fh := range fhs {
71 fh.tmpshared = true
72 }
73 }
74 }
75 if err != nil {
76 form.RemoveAll()
77 if file != nil {
78 os.Remove(file.Name())
79 }
80 }
81 }()
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 maxFileMemoryBytes := maxMemory
97 if maxFileMemoryBytes == math.MaxInt64 {
98 maxFileMemoryBytes--
99 }
100 maxMemoryBytes := maxMemory + int64(10<<20)
101 if maxMemoryBytes <= 0 {
102 if maxMemory < 0 {
103 maxMemoryBytes = 0
104 } else {
105 maxMemoryBytes = math.MaxInt64
106 }
107 }
108 var copyBuf []byte
109 for {
110 p, err := r.nextPart(false, maxMemoryBytes, maxHeaders)
111 if err == io.EOF {
112 break
113 }
114 if err != nil {
115 return nil, err
116 }
117 if maxParts <= 0 {
118 return nil, ErrMessageTooLarge
119 }
120 maxParts--
121
122 name := p.FormName()
123 if name == "" {
124 continue
125 }
126 filename := p.FileName()
127
128
129
130
131 const mapEntryOverhead = 200
132 maxMemoryBytes -= int64(len(name))
133 maxMemoryBytes -= mapEntryOverhead
134 if maxMemoryBytes < 0 {
135
136
137 return nil, ErrMessageTooLarge
138 }
139
140 var b bytes.Buffer
141
142 if filename == "" {
143
144 n, err := io.CopyN(&b, p, maxMemoryBytes+1)
145 if err != nil && err != io.EOF {
146 return nil, err
147 }
148 maxMemoryBytes -= n
149 if maxMemoryBytes < 0 {
150 return nil, ErrMessageTooLarge
151 }
152 form.Value[name] = append(form.Value[name], b.String())
153 continue
154 }
155
156
157 const fileHeaderSize = 100
158 maxMemoryBytes -= mimeHeaderSize(p.Header)
159 maxMemoryBytes -= mapEntryOverhead
160 maxMemoryBytes -= fileHeaderSize
161 if maxMemoryBytes < 0 {
162 return nil, ErrMessageTooLarge
163 }
164 for _, v := range p.Header {
165 maxHeaders -= int64(len(v))
166 }
167 fh := &FileHeader{
168 Filename: filename,
169 Header: p.Header,
170 }
171 n, err := io.CopyN(&b, p, maxFileMemoryBytes+1)
172 if err != nil && err != io.EOF {
173 return nil, err
174 }
175 if n > maxFileMemoryBytes {
176 if file == nil {
177 file, err = os.CreateTemp(r.tempDir, "multipart-")
178 if err != nil {
179 return nil, err
180 }
181 }
182 numDiskFiles++
183 if _, err := file.Write(b.Bytes()); err != nil {
184 return nil, err
185 }
186 if copyBuf == nil {
187 copyBuf = make([]byte, 32*1024)
188 }
189
190 type writerOnly struct{ io.Writer }
191 remainingSize, err := io.CopyBuffer(writerOnly{file}, p, copyBuf)
192 if err != nil {
193 return nil, err
194 }
195 fh.tmpfile = file.Name()
196 fh.Size = int64(b.Len()) + remainingSize
197 fh.tmpoff = fileOff
198 fileOff += fh.Size
199 if !combineFiles {
200 if err := file.Close(); err != nil {
201 return nil, err
202 }
203 file = nil
204 }
205 } else {
206 fh.content = b.Bytes()
207 fh.Size = int64(len(fh.content))
208 maxFileMemoryBytes -= n
209 maxMemoryBytes -= n
210 }
211 form.File[name] = append(form.File[name], fh)
212 }
213
214 return form, nil
215 }
216
217 func mimeHeaderSize(h textproto.MIMEHeader) (size int64) {
218 size = 400
219 for k, vs := range h {
220 size += int64(len(k))
221 size += 200
222 for _, v := range vs {
223 size += int64(len(v))
224 }
225 }
226 return size
227 }
228
229
230
231
232
233
234 type Form struct {
235 Value map[string][]string
236 File map[string][]*FileHeader
237 }
238
239
240 func (f *Form) RemoveAll() error {
241 var err error
242 for _, fhs := range f.File {
243 for _, fh := range fhs {
244 if fh.tmpfile != "" {
245 e := os.Remove(fh.tmpfile)
246 if e != nil && !errors.Is(e, os.ErrNotExist) && err == nil {
247 err = e
248 }
249 }
250 }
251 }
252 return err
253 }
254
255
256 type FileHeader struct {
257 Filename string
258 Header textproto.MIMEHeader
259 Size int64
260
261 content []byte
262 tmpfile string
263 tmpoff int64
264 tmpshared bool
265 }
266
267
268 func (fh *FileHeader) Open() (File, error) {
269 if b := fh.content; b != nil {
270 r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
271 return sectionReadCloser{r, nil}, nil
272 }
273 if fh.tmpshared {
274 f, err := os.Open(fh.tmpfile)
275 if err != nil {
276 return nil, err
277 }
278 r := io.NewSectionReader(f, fh.tmpoff, fh.Size)
279 return sectionReadCloser{r, f}, nil
280 }
281 return os.Open(fh.tmpfile)
282 }
283
284
285
286
287 type File interface {
288 io.Reader
289 io.ReaderAt
290 io.Seeker
291 io.Closer
292 }
293
294
295
296 type sectionReadCloser struct {
297 *io.SectionReader
298 io.Closer
299 }
300
301 func (rc sectionReadCloser) Close() error {
302 if rc.Closer != nil {
303 return rc.Closer.Close()
304 }
305 return nil
306 }
307
View as plain text