...

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

View as plain text