github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/src/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 "io/ioutil" 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 for { 45 p, err := r.NextPart() 46 if err == io.EOF { 47 break 48 } 49 if err != nil { 50 return nil, err 51 } 52 53 name := p.FormName() 54 if name == "" { 55 continue 56 } 57 filename := p.FileName() 58 59 var b bytes.Buffer 60 61 _, hasContentTypeHeader := p.Header["Content-Type"] 62 if !hasContentTypeHeader && filename == "" { 63 // value, store as string in memory 64 n, err := io.CopyN(&b, p, maxValueBytes+1) 65 if err != nil && err != io.EOF { 66 return nil, err 67 } 68 maxValueBytes -= n 69 if maxValueBytes < 0 { 70 return nil, ErrMessageTooLarge 71 } 72 form.Value[name] = append(form.Value[name], b.String()) 73 continue 74 } 75 76 // file, store in memory or on disk 77 fh := &FileHeader{ 78 Filename: filename, 79 Header: p.Header, 80 } 81 n, err := io.CopyN(&b, p, maxMemory+1) 82 if err != nil && err != io.EOF { 83 return nil, err 84 } 85 if n > maxMemory { 86 // too big, write to disk and flush buffer 87 file, err := ioutil.TempFile("", "multipart-") 88 if err != nil { 89 return nil, err 90 } 91 size, err := io.Copy(file, io.MultiReader(&b, p)) 92 if cerr := file.Close(); err == nil { 93 err = cerr 94 } 95 if err != nil { 96 os.Remove(file.Name()) 97 return nil, err 98 } 99 fh.tmpfile = file.Name() 100 fh.Size = size 101 } else { 102 fh.content = b.Bytes() 103 fh.Size = int64(len(fh.content)) 104 maxMemory -= n 105 maxValueBytes -= n 106 } 107 form.File[name] = append(form.File[name], fh) 108 } 109 110 return form, nil 111 } 112 113 // Form is a parsed multipart form. 114 // Its File parts are stored either in memory or on disk, 115 // and are accessible via the *FileHeader's Open method. 116 // Its Value parts are stored as strings. 117 // Both are keyed by field name. 118 type Form struct { 119 Value map[string][]string 120 File map[string][]*FileHeader 121 } 122 123 // RemoveAll removes any temporary files associated with a Form. 124 func (f *Form) RemoveAll() error { 125 var err error 126 for _, fhs := range f.File { 127 for _, fh := range fhs { 128 if fh.tmpfile != "" { 129 e := os.Remove(fh.tmpfile) 130 if e != nil && err == nil { 131 err = e 132 } 133 } 134 } 135 } 136 return err 137 } 138 139 // A FileHeader describes a file part of a multipart request. 140 type FileHeader struct { 141 Filename string 142 Header textproto.MIMEHeader 143 Size int64 144 145 content []byte 146 tmpfile string 147 } 148 149 // Open opens and returns the FileHeader's associated File. 150 func (fh *FileHeader) Open() (File, error) { 151 if b := fh.content; b != nil { 152 r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))) 153 return sectionReadCloser{r}, nil 154 } 155 return os.Open(fh.tmpfile) 156 } 157 158 // File is an interface to access the file part of a multipart message. 159 // Its contents may be either stored in memory or on disk. 160 // If stored on disk, the File's underlying concrete type will be an *os.File. 161 type File interface { 162 io.Reader 163 io.ReaderAt 164 io.Seeker 165 io.Closer 166 } 167 168 // helper types to turn a []byte into a File 169 170 type sectionReadCloser struct { 171 *io.SectionReader 172 } 173 174 func (rc sectionReadCloser) Close() error { 175 return nil 176 }