summaryrefslogtreecommitdiff
path: root/libgo/go/mime/multipart
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2016-07-22 18:15:38 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2016-07-22 18:15:38 +0000
commit22b955cca564a9a3a5b8c9d9dd1e295b7943c128 (patch)
treeabdbd898676e1f853fca2d7e031d105d7ebcf676 /libgo/go/mime/multipart
parent9d04a3af4c6491536badf6bde9707c907e4d196b (diff)
downloadgcc-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.go6
-rw-r--r--libgo/go/mime/multipart/formdata_test.go36
-rw-r--r--libgo/go/mime/multipart/multipart.go28
-rw-r--r--libgo/go/mime/multipart/multipart_test.go4
-rw-r--r--libgo/go/mime/multipart/writer.go13
-rw-r--r--libgo/go/mime/multipart/writer_test.go30
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)
+ }
+}