...

Source file src/mime/multipart/formdata.go

Documentation: mime/multipart

		 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  }
	 183  

View as plain text