Source file src/mime/multipart/formdata.go

     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