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