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 if maxMemory < 0 {
97 maxMemory = 0
98 }
99 maxFileMemoryBytes := maxMemory
100 if maxFileMemoryBytes == math.MaxInt64 {
101 maxFileMemoryBytes--
102 }
103 maxMemoryBytes := maxMemory + int64(10<<20)
104 if maxMemoryBytes <= 0 {
105 maxMemoryBytes = math.MaxInt64
106 }
107 var copyBuf []byte
108 for {
109 p, err := r.nextPart(false, maxMemoryBytes, maxHeaders)
110 if err == io.EOF {
111 break
112 }
113 if err != nil {
114 return nil, err
115 }
116 if maxParts <= 0 {
117 return nil, ErrMessageTooLarge
118 }
119 maxParts--
120
121 name := p.FormName()
122 if name == "" {
123 continue
124 }
125 filename := p.FileName()
126
127
128
129
130 const mapEntryOverhead = 200
131 maxMemoryBytes -= int64(len(name))
132 maxMemoryBytes -= mapEntryOverhead
133 if maxMemoryBytes < 0 {
134
135
136 return nil, ErrMessageTooLarge
137 }
138
139 var b bytes.Buffer
140
141 if filename == "" {
142
143 n, err := io.CopyN(&b, p, maxMemoryBytes+1)
144 if err != nil && err != io.EOF {
145 return nil, err
146 }
147 maxMemoryBytes -= n
148 if maxMemoryBytes < 0 {
149 return nil, ErrMessageTooLarge
150 }
151 form.Value[name] = append(form.Value[name], b.String())
152 continue
153 }
154
155
156 const fileHeaderSize = 100
157 maxMemoryBytes -= mimeHeaderSize(p.Header)
158 maxMemoryBytes -= mapEntryOverhead
159 maxMemoryBytes -= fileHeaderSize
160 if maxMemoryBytes < 0 {
161 return nil, ErrMessageTooLarge
162 }
163 for _, v := range p.Header {
164 maxHeaders -= int64(len(v))
165 }
166 fh := &FileHeader{
167 Filename: filename,
168 Header: p.Header,
169 }
170 n, err := io.CopyN(&b, p, maxFileMemoryBytes+1)
171 if err != nil && err != io.EOF {
172 return nil, err
173 }
174 if n > maxFileMemoryBytes {
175 if file == nil {
176 file, err = os.CreateTemp(r.tempDir, "multipart-")
177 if err != nil {
178 return nil, err
179 }
180 }
181 numDiskFiles++
182 if _, err := file.Write(b.Bytes()); err != nil {
183 return nil, err
184 }
185 if copyBuf == nil {
186 copyBuf = make([]byte, 32*1024)
187 }
188
189 type writerOnly struct{ io.Writer }
190 remainingSize, err := io.CopyBuffer(writerOnly{file}, p, copyBuf)
191 if err != nil {
192 return nil, err
193 }
194 fh.tmpfile = file.Name()
195 fh.Size = int64(b.Len()) + remainingSize
196 fh.tmpoff = fileOff
197 fileOff += fh.Size
198 if !combineFiles {
199 if err := file.Close(); err != nil {
200 return nil, err
201 }
202 file = nil
203 }
204 } else {
205 fh.content = b.Bytes()
206 fh.Size = int64(len(fh.content))
207 maxFileMemoryBytes -= n
208 maxMemoryBytes -= n
209 }
210 form.File[name] = append(form.File[name], fh)
211 }
212
213 return form, nil
214 }
215
216 func mimeHeaderSize(h textproto.MIMEHeader) (size int64) {
217 size = 400
218 for k, vs := range h {
219 size += int64(len(k))
220 size += 200
221 for _, v := range vs {
222 size += int64(len(v))
223 }
224 }
225 return size
226 }
227
228
229
230
231
232
233 type Form struct {
234 Value map[string][]string
235 File map[string][]*FileHeader
236 }
237
238
239 func (f *Form) RemoveAll() error {
240 var err error
241 for _, fhs := range f.File {
242 for _, fh := range fhs {
243 if fh.tmpfile != "" {
244 e := os.Remove(fh.tmpfile)
245 if e != nil && !errors.Is(e, os.ErrNotExist) && err == nil {
246 err = e
247 }
248 }
249 }
250 }
251 return err
252 }
253
254
255 type FileHeader struct {
256 Filename string
257 Header textproto.MIMEHeader
258 Size int64
259
260 content []byte
261 tmpfile string
262 tmpoff int64
263 tmpshared bool
264 }
265
266
267 func (fh *FileHeader) Open() (File, error) {
268 if b := fh.content; b != nil {
269 r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
270 return sectionReadCloser{r, nil}, nil
271 }
272 if fh.tmpshared {
273 f, err := os.Open(fh.tmpfile)
274 if err != nil {
275 return nil, err
276 }
277 r := io.NewSectionReader(f, fh.tmpoff, fh.Size)
278 return sectionReadCloser{r, f}, nil
279 }
280 return os.Open(fh.tmpfile)
281 }
282
283
284
285
286 type File interface {
287 io.Reader
288 io.ReaderAt
289 io.Seeker
290 io.Closer
291 }
292
293
294
295 type sectionReadCloser struct {
296 *io.SectionReader
297 io.Closer
298 }
299
300 func (rc sectionReadCloser) Close() error {
301 if rc.Closer != nil {
302 return rc.Closer.Close()
303 }
304 return nil
305 }
306
View as plain text