github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/mime/multipart/formdata.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 "bytes" 9 "errors" 10 "io" 11 "math" 12 "net/textproto" 13 "os" 14 ) 15 16 // ErrMessageTooLarge is returned by ReadForm if the message form 17 // data is too large to be processed. 18 var ErrMessageTooLarge = errors.New("multipart: message too large") 19 20 // TODO(adg,bradfitz): find a way to unify the DoS-prevention strategy here 21 // with that of the http package's ParseForm. 22 23 // ReadForm parses an entire multipart message whose parts have 24 // a Content-Disposition of "form-data". 25 // It stores up to maxMemory bytes + 10MB (reserved for non-file parts) 26 // in memory. File parts which can't be stored in memory will be stored on 27 // disk in temporary files. 28 // It returns ErrMessageTooLarge if all non-file parts can't be stored in 29 // memory. 30 func (r *Reader) ReadForm(maxMemory int64) (*Form, error) { 31 return r.readForm(maxMemory) 32 } 33 34 func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { 35 form := &Form{make(map[string][]string), make(map[string][]*FileHeader)} 36 defer func() { 37 if err != nil { 38 form.RemoveAll() 39 } 40 }() 41 42 // Reserve an additional 10 MB for non-file parts. 43 maxValueBytes := maxMemory + int64(10<<20) 44 if maxValueBytes <= 0 { 45 if maxMemory < 0 { 46 maxValueBytes = 0 47 } else { 48 maxValueBytes = math.MaxInt64 49 } 50 } 51 for { 52 p, err := r.NextPart() 53 if err == io.EOF { 54 break 55 } 56 if err != nil { 57 return nil, err 58 } 59 60 name := p.FormName() 61 if name == "" { 62 continue 63 } 64 filename := p.FileName() 65 66 var b bytes.Buffer 67 68 if filename == "" { 69 // value, store as string in memory 70 n, err := io.CopyN(&b, p, maxValueBytes+1) 71 if err != nil && err != io.EOF { 72 return nil, err 73 } 74 maxValueBytes -= n 75 if maxValueBytes < 0 { 76 return nil, ErrMessageTooLarge 77 } 78 form.Value[name] = append(form.Value[name], b.String()) 79 continue 80 } 81 82 // file, store in memory or on disk 83 fh := &FileHeader{ 84 Filename: filename, 85 Header: p.Header, 86 } 87 n, err := io.CopyN(&b, p, maxMemory+1) 88 if err != nil && err != io.EOF { 89 return nil, err 90 } 91 if n > maxMemory { 92 // too big, write to disk and flush buffer 93 file, err := os.CreateTemp("", "multipart-") 94 if err != nil { 95 return nil, err 96 } 97 size, err := io.Copy(file, io.MultiReader(&b, p)) 98 if cerr := file.Close(); err == nil { 99 err = cerr 100 } 101 if err != nil { 102 os.Remove(file.Name()) 103 return nil, err 104 } 105 fh.tmpfile = file.Name() 106 fh.Size = size 107 } else { 108 fh.content = b.Bytes() 109 fh.Size = int64(len(fh.content)) 110 maxMemory -= n 111 maxValueBytes -= n 112 } 113 form.File[name] = append(form.File[name], fh) 114 } 115 116 return form, nil 117 } 118 119 // Form is a parsed multipart form. 120 // Its File parts are stored either in memory or on disk, 121 // and are accessible via the *FileHeader's Open method. 122 // Its Value parts are stored as strings. 123 // Both are keyed by field name. 124 type Form struct { 125 Value map[string][]string 126 File map[string][]*FileHeader 127 } 128 129 // RemoveAll removes any temporary files associated with a Form. 130 func (f *Form) RemoveAll() error { 131 var err error 132 for _, fhs := range f.File { 133 for _, fh := range fhs { 134 if fh.tmpfile != "" { 135 e := os.Remove(fh.tmpfile) 136 if e != nil && err == nil { 137 err = e 138 } 139 } 140 } 141 } 142 return err 143 } 144 145 // A FileHeader describes a file part of a multipart request. 146 type FileHeader struct { 147 Filename string 148 Header textproto.MIMEHeader 149 Size int64 150 151 content []byte 152 tmpfile string 153 } 154 155 // Open opens and returns the FileHeader's associated File. 156 func (fh *FileHeader) Open() (File, error) { 157 if b := fh.content; b != nil { 158 r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))) 159 return sectionReadCloser{r}, nil 160 } 161 return os.Open(fh.tmpfile) 162 } 163 164 // File is an interface to access the file part of a multipart message. 165 // Its contents may be either stored in memory or on disk. 166 // If stored on disk, the File's underlying concrete type will be an *os.File. 167 type File interface { 168 io.Reader 169 io.ReaderAt 170 io.Seeker 171 io.Closer 172 } 173 174 // helper types to turn a []byte into a File 175 176 type sectionReadCloser struct { 177 *io.SectionReader 178 } 179 180 func (rc sectionReadCloser) Close() error { 181 return nil 182 }