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