]> Cypherpunks repositories - gostls13.git/commitdiff
mime/multipart: sort header keys to ensure reproducible output
authorSimon Thulbourn <simon+github@thulbourn.com>
Mon, 7 Dec 2015 16:36:11 +0000 (16:36 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 16 May 2016 22:55:16 +0000 (22:55 +0000)
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 <bradfitz@golang.org>
src/mime/multipart/writer.go
src/mime/multipart/writer_test.go

index 80960939d62ecb868fa639f427162ae0df5f5ee2..f82756d55185e2eb4a0cda34dafe98ed192bd748 100644 (file)
@@ -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)
                }
        }
index ba00c97eceea4b6f302a08ce1bd6fb70c8a777cf..9670c660a4a0b72d7c5f544b9ce4783efcf82d54 100644 (file)
@@ -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)
+       }
+}