diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-07-22 18:15:38 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-07-22 18:15:38 +0000 |
commit | 22b955cca564a9a3a5b8c9d9dd1e295b7943c128 (patch) | |
tree | abdbd898676e1f853fca2d7e031d105d7ebcf676 /libgo/go/mime/multipart | |
parent | 9d04a3af4c6491536badf6bde9707c907e4d196b (diff) | |
download | gcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.tar.gz |
libgo: update to go1.7rc3
Reviewed-on: https://go-review.googlesource.com/25150
From-SVN: r238662
Diffstat (limited to 'libgo/go/mime/multipart')
-rw-r--r-- | libgo/go/mime/multipart/formdata.go | 6 | ||||
-rw-r--r-- | libgo/go/mime/multipart/formdata_test.go | 36 | ||||
-rw-r--r-- | libgo/go/mime/multipart/multipart.go | 28 | ||||
-rw-r--r-- | libgo/go/mime/multipart/multipart_test.go | 4 | ||||
-rw-r--r-- | libgo/go/mime/multipart/writer.go | 13 | ||||
-rw-r--r-- | libgo/go/mime/multipart/writer_test.go | 30 |
6 files changed, 105 insertions, 12 deletions
diff --git a/libgo/go/mime/multipart/formdata.go b/libgo/go/mime/multipart/formdata.go index eee53fc8dd0..8085bd3975b 100644 --- a/libgo/go/mime/multipart/formdata.go +++ b/libgo/go/mime/multipart/formdata.go @@ -20,7 +20,11 @@ import ( // a Content-Disposition of "form-data". // It stores up to maxMemory bytes of the file parts in memory // and the remainder on disk in temporary files. -func (r *Reader) ReadForm(maxMemory int64) (f *Form, err error) { +func (r *Reader) ReadForm(maxMemory int64) (*Form, error) { + return r.readForm(maxMemory) +} + +func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { form := &Form{make(map[string][]string), make(map[string][]*FileHeader)} defer func() { if err != nil { diff --git a/libgo/go/mime/multipart/formdata_test.go b/libgo/go/mime/multipart/formdata_test.go index 6e2388bafef..1deca0b94df 100644 --- a/libgo/go/mime/multipart/formdata_test.go +++ b/libgo/go/mime/multipart/formdata_test.go @@ -88,3 +88,39 @@ Content-Disposition: form-data; name="textb" ` + textbValue + ` --MyBoundary-- ` + +func TestReadForm_NoReadAfterEOF(t *testing.T) { + maxMemory := int64(32) << 20 + boundary := `---------------------------8d345eef0d38dc9` + body := ` +-----------------------------8d345eef0d38dc9 +Content-Disposition: form-data; name="version" + +171 +-----------------------------8d345eef0d38dc9--` + + mr := NewReader(&failOnReadAfterErrorReader{t: t, r: strings.NewReader(body)}, boundary) + + f, err := mr.ReadForm(maxMemory) + if err != nil { + t.Fatal(err) + } + t.Logf("Got: %#v", f) +} + +// failOnReadAfterErrorReader is an io.Reader wrapping r. +// It fails t if any Read is called after a failing Read. +type failOnReadAfterErrorReader struct { + t *testing.T + r io.Reader + sawErr error +} + +func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) { + if r.sawErr != nil { + r.t.Fatalf("unexpected Read on Reader after previous read saw error %v", r.sawErr) + } + n, err = r.r.Read(p) + r.sawErr = err + return +} diff --git a/libgo/go/mime/multipart/multipart.go b/libgo/go/mime/multipart/multipart.go index 3b746a5e157..205348ca89d 100644 --- a/libgo/go/mime/multipart/multipart.go +++ b/libgo/go/mime/multipart/multipart.go @@ -96,7 +96,7 @@ func (p *Part) parseContentDisposition() { func NewReader(r io.Reader, boundary string) *Reader { b := []byte("\r\n--" + boundary + "--") return &Reader{ - bufReader: bufio.NewReaderSize(r, peekBufferSize), + bufReader: bufio.NewReaderSize(&stickyErrorReader{r: r}, peekBufferSize), nl: b[:2], nlDashBoundary: b[:len(b)-2], dashBoundaryDash: b[2:], @@ -104,6 +104,24 @@ func NewReader(r io.Reader, boundary string) *Reader { } } +// stickyErrorReader is an io.Reader which never calls Read on its +// underlying Reader once an error has been seen. (the io.Reader +// interface's contract promises nothing about the return values of +// Read calls after an error, yet this package does do multiple Reads +// after error) +type stickyErrorReader struct { + r io.Reader + err error +} + +func (r *stickyErrorReader) Read(p []byte) (n int, _ error) { + if r.err != nil { + return 0, r.err + } + n, r.err = r.r.Read(p) + return n, r.err +} + func newPart(mr *Reader) (*Part, error) { bp := &Part{ Header: make(map[string][]string), @@ -150,13 +168,13 @@ func (pr partReader) Read(d []byte) (n int, err error) { }() if p.buffer.Len() >= len(d) { // Internal buffer of unconsumed data is large enough for - // the read request. No need to parse more at the moment. + // the read request. No need to parse more at the moment. return p.buffer.Read(d) } peek, err := p.mr.bufReader.Peek(peekBufferSize) // TODO(bradfitz): add buffer size accessor // Look for an immediate empty part without a leading \r\n - // before the boundary separator. Some MIME code makes empty + // before the boundary separator. Some MIME code makes empty // parts like this. Most browsers, however, write the \r\n // before the subsequent boundary even for empty parts and // won't hit this path. @@ -210,7 +228,7 @@ func (p *Part) Close() error { } // Reader is an iterator over parts in a MIME multipart body. -// Reader's underlying parser consumes its input as needed. Seeking +// Reader's underlying parser consumes its input as needed. Seeking // isn't supported. type Reader struct { bufReader *bufio.Reader @@ -310,7 +328,7 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) { rest = skipLWSPChar(rest) // On the first part, see our lines are ending in \n instead of \r\n - // and switch into that mode if so. This is a violation of the spec, + // and switch into that mode if so. This is a violation of the spec, // but occurs in practice. if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' { mr.nl = mr.nl[1:] diff --git a/libgo/go/mime/multipart/multipart_test.go b/libgo/go/mime/multipart/multipart_test.go index d06bb4159aa..82a7f86e67c 100644 --- a/libgo/go/mime/multipart/multipart_test.go +++ b/libgo/go/mime/multipart/multipart_test.go @@ -446,8 +446,8 @@ type parseTest struct { var parseTests = []parseTest{ // Actual body from App Engine on a blob upload. The final part (the // Content-Type: message/external-body) is what App Engine replaces - // the uploaded file with. The other form fields (prefixed with - // "other" in their form-data name) are unchanged. A bug was + // the uploaded file with. The other form fields (prefixed with + // "other" in their form-data name) are unchanged. A bug was // reported with blob uploads failing when the other fields were // empty. This was the MIME POST body that previously failed. { diff --git a/libgo/go/mime/multipart/writer.go b/libgo/go/mime/multipart/writer.go index 80960939d62..f82756d5518 100644 --- a/libgo/go/mime/multipart/writer.go +++ b/libgo/go/mime/multipart/writer.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "net/textproto" + "sort" "strings" ) @@ -94,10 +95,14 @@ func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) { } else { fmt.Fprintf(&b, "--%s\r\n", w.boundary) } - // TODO(bradfitz): move this to textproto.MimeHeader.Write(w), have it sort - // and clean, like http.Header.Write(w) does. - for k, vv := range header { - for _, v := range vv { + + keys := make([]string, 0, len(header)) + for k := range header { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + for _, v := range header[k] { fmt.Fprintf(&b, "%s: %s\r\n", k, v) } } diff --git a/libgo/go/mime/multipart/writer_test.go b/libgo/go/mime/multipart/writer_test.go index ba00c97ecee..9670c660a4a 100644 --- a/libgo/go/mime/multipart/writer_test.go +++ b/libgo/go/mime/multipart/writer_test.go @@ -7,6 +7,7 @@ package multipart import ( "bytes" "io/ioutil" + "net/textproto" "strings" "testing" ) @@ -126,3 +127,32 @@ func TestWriterBoundaryGoroutines(t *testing.T) { w.Boundary() <-done } + +func TestSortedHeader(t *testing.T) { + var buf bytes.Buffer + w := NewWriter(&buf) + if err := w.SetBoundary("MIMEBOUNDARY"); err != nil { + t.Fatalf("Error setting mime boundary: %v", err) + } + + header := textproto.MIMEHeader{ + "A": {"2"}, + "B": {"5", "7", "6"}, + "C": {"4"}, + "M": {"3"}, + "Z": {"1"}, + } + + part, err := w.CreatePart(header) + if err != nil { + t.Fatalf("Unable to create part: %v", err) + } + part.Write([]byte("foo")) + + w.Close() + + want := "--MIMEBOUNDARY\r\nA: 2\r\nB: 5\r\nB: 7\r\nB: 6\r\nC: 4\r\nM: 3\r\nZ: 1\r\n\r\nfoo\r\n--MIMEBOUNDARY--\r\n" + if want != buf.String() { + t.Fatalf("\n got: %q\nwant: %q\n", buf.String(), want) + } +} |