Source file src/mime/multipart/formdata_test.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  	"io"
    10  	"math"
    11  	"os"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  func TestReadForm(t *testing.T) {
    17  	b := strings.NewReader(strings.ReplaceAll(message, "\n", "\r\n"))
    18  	r := NewReader(b, boundary)
    19  	f, err := r.ReadForm(25)
    20  	if err != nil {
    21  		t.Fatal("ReadForm:", err)
    22  	}
    23  	defer f.RemoveAll()
    24  	if g, e := f.Value["texta"][0], textaValue; g != e {
    25  		t.Errorf("texta value = %q, want %q", g, e)
    26  	}
    27  	if g, e := f.Value["textb"][0], textbValue; g != e {
    28  		t.Errorf("texta value = %q, want %q", g, e)
    29  	}
    30  	fd := testFile(t, f.File["filea"][0], "filea.txt", fileaContents)
    31  	if _, ok := fd.(*os.File); ok {
    32  		t.Error("file is *os.File, should not be")
    33  	}
    34  	fd.Close()
    35  	fd = testFile(t, f.File["fileb"][0], "fileb.txt", filebContents)
    36  	if _, ok := fd.(*os.File); !ok {
    37  		t.Errorf("file has unexpected underlying type %T", fd)
    38  	}
    39  	fd.Close()
    40  }
    41  
    42  func TestReadFormWithNamelessFile(t *testing.T) {
    43  	b := strings.NewReader(strings.ReplaceAll(messageWithFileWithoutName, "\n", "\r\n"))
    44  	r := NewReader(b, boundary)
    45  	f, err := r.ReadForm(25)
    46  	if err != nil {
    47  		t.Fatal("ReadForm:", err)
    48  	}
    49  	defer f.RemoveAll()
    50  
    51  	if g, e := f.Value["hiddenfile"][0], filebContents; g != e {
    52  		t.Errorf("hiddenfile value = %q, want %q", g, e)
    53  	}
    54  }
    55  
    56  // Issue 40430: Handle ReadForm(math.MaxInt64)
    57  func TestReadFormMaxMemoryOverflow(t *testing.T) {
    58  	b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
    59  	r := NewReader(b, boundary)
    60  	f, err := r.ReadForm(math.MaxInt64)
    61  	if err != nil {
    62  		t.Fatalf("ReadForm(MaxInt64): %v", err)
    63  	}
    64  	if f == nil {
    65  		t.Fatal("ReadForm(MaxInt64): missing form")
    66  	}
    67  }
    68  
    69  func TestReadFormWithTextContentType(t *testing.T) {
    70  	// From https://github.com/golang/go/issues/24041
    71  	b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
    72  	r := NewReader(b, boundary)
    73  	f, err := r.ReadForm(25)
    74  	if err != nil {
    75  		t.Fatal("ReadForm:", err)
    76  	}
    77  	defer f.RemoveAll()
    78  
    79  	if g, e := f.Value["texta"][0], textaValue; g != e {
    80  		t.Errorf("texta value = %q, want %q", g, e)
    81  	}
    82  }
    83  
    84  func testFile(t *testing.T, fh *FileHeader, efn, econtent string) File {
    85  	if fh.Filename != efn {
    86  		t.Errorf("filename = %q, want %q", fh.Filename, efn)
    87  	}
    88  	if fh.Size != int64(len(econtent)) {
    89  		t.Errorf("size = %d, want %d", fh.Size, len(econtent))
    90  	}
    91  	f, err := fh.Open()
    92  	if err != nil {
    93  		t.Fatal("opening file:", err)
    94  	}
    95  	b := new(bytes.Buffer)
    96  	_, err = io.Copy(b, f)
    97  	if err != nil {
    98  		t.Fatal("copying contents:", err)
    99  	}
   100  	if g := b.String(); g != econtent {
   101  		t.Errorf("contents = %q, want %q", g, econtent)
   102  	}
   103  	return f
   104  }
   105  
   106  const (
   107  	fileaContents = "This is a test file."
   108  	filebContents = "Another test file."
   109  	textaValue    = "foo"
   110  	textbValue    = "bar"
   111  	boundary      = `MyBoundary`
   112  )
   113  
   114  const messageWithFileWithoutName = `
   115  --MyBoundary
   116  Content-Disposition: form-data; name="hiddenfile"; filename=""
   117  Content-Type: text/plain
   118  
   119  ` + filebContents + `
   120  --MyBoundary--
   121  `
   122  
   123  const messageWithTextContentType = `
   124  --MyBoundary
   125  Content-Disposition: form-data; name="texta"
   126  Content-Type: text/plain
   127  
   128  ` + textaValue + `
   129  --MyBoundary
   130  `
   131  
   132  const message = `
   133  --MyBoundary
   134  Content-Disposition: form-data; name="filea"; filename="filea.txt"
   135  Content-Type: text/plain
   136  
   137  ` + fileaContents + `
   138  --MyBoundary
   139  Content-Disposition: form-data; name="fileb"; filename="fileb.txt"
   140  Content-Type: text/plain
   141  
   142  ` + filebContents + `
   143  --MyBoundary
   144  Content-Disposition: form-data; name="texta"
   145  
   146  ` + textaValue + `
   147  --MyBoundary
   148  Content-Disposition: form-data; name="textb"
   149  
   150  ` + textbValue + `
   151  --MyBoundary--
   152  `
   153  
   154  func TestReadForm_NoReadAfterEOF(t *testing.T) {
   155  	maxMemory := int64(32) << 20
   156  	boundary := `---------------------------8d345eef0d38dc9`
   157  	body := `
   158  -----------------------------8d345eef0d38dc9
   159  Content-Disposition: form-data; name="version"
   160  
   161  171
   162  -----------------------------8d345eef0d38dc9--`
   163  
   164  	mr := NewReader(&failOnReadAfterErrorReader{t: t, r: strings.NewReader(body)}, boundary)
   165  
   166  	f, err := mr.ReadForm(maxMemory)
   167  	if err != nil {
   168  		t.Fatal(err)
   169  	}
   170  	t.Logf("Got: %#v", f)
   171  }
   172  
   173  // failOnReadAfterErrorReader is an io.Reader wrapping r.
   174  // It fails t if any Read is called after a failing Read.
   175  type failOnReadAfterErrorReader struct {
   176  	t      *testing.T
   177  	r      io.Reader
   178  	sawErr error
   179  }
   180  
   181  func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) {
   182  	if r.sawErr != nil {
   183  		r.t.Fatalf("unexpected Read on Reader after previous read saw error %v", r.sawErr)
   184  	}
   185  	n, err = r.r.Read(p)
   186  	r.sawErr = err
   187  	return
   188  }
   189  
   190  // TestReadForm_NonFileMaxMemory asserts that the ReadForm maxMemory limit is applied
   191  // while processing non-file form data as well as file form data.
   192  func TestReadForm_NonFileMaxMemory(t *testing.T) {
   193  	n := 10<<20 + 25
   194  	if testing.Short() {
   195  		n = 10<<10 + 25
   196  	}
   197  	largeTextValue := strings.Repeat("1", n)
   198  	message := `--MyBoundary
   199  Content-Disposition: form-data; name="largetext"
   200  
   201  ` + largeTextValue + `
   202  --MyBoundary--
   203  `
   204  
   205  	testBody := strings.ReplaceAll(message, "\n", "\r\n")
   206  	testCases := []struct {
   207  		name      string
   208  		maxMemory int64
   209  		err       error
   210  	}{
   211  		{"smaller", 50, nil},
   212  		{"exact-fit", 25, nil},
   213  		{"too-large", 0, ErrMessageTooLarge},
   214  	}
   215  	for _, tc := range testCases {
   216  		t.Run(tc.name, func(t *testing.T) {
   217  			if tc.maxMemory == 0 && testing.Short() {
   218  				t.Skip("skipping in -short mode")
   219  			}
   220  			b := strings.NewReader(testBody)
   221  			r := NewReader(b, boundary)
   222  			f, err := r.ReadForm(tc.maxMemory)
   223  			if err == nil {
   224  				defer f.RemoveAll()
   225  			}
   226  			if tc.err != err {
   227  				t.Fatalf("ReadForm error - got: %v; expected: %v", tc.err, err)
   228  			}
   229  			if err == nil {
   230  				if g := f.Value["largetext"][0]; g != largeTextValue {
   231  					t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue))
   232  				}
   233  			}
   234  		})
   235  	}
   236  }
   237  

View as plain text