From: Simon Thulbourn Date: Mon, 7 Dec 2015 16:36:11 +0000 (+0000) Subject: mime/multipart: sort header keys to ensure reproducible output X-Git-Tag: go1.7beta1~198 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=28c201860e0838b10972e805582007f9eb61e7ac;p=gostls13.git mime/multipart: sort header keys to ensure reproducible output Adds a transparent sort to the mime/multipart package, which is only used in the CreatePart func. This will ensure the ordering of the MIMEHeader. The point of this change was to ensure the output would be consistent and something that could be depended on. Fixes #13522 Change-Id: I9584ef9dbe98ce97d536d897326914653f8d9ddf Reviewed-on: https://go-review.googlesource.com/17497 Reviewed-by: Brad Fitzpatrick --- diff --git a/src/mime/multipart/writer.go b/src/mime/multipart/writer.go index 80960939d6..f82756d551 100644 --- a/src/mime/multipart/writer.go +++ b/src/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/src/mime/multipart/writer_test.go b/src/mime/multipart/writer_test.go index ba00c97ece..9670c660a4 100644 --- a/src/mime/multipart/writer_test.go +++ b/src/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) + } +}