]> Cypherpunks repositories - gostls13.git/commitdiff
mime/multipart: misc code/doc fixes
authorBrad Fitzpatrick <bradfitz@golang.org>
Thu, 26 May 2011 02:21:05 +0000 (19:21 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 26 May 2011 02:21:05 +0000 (19:21 -0700)
R=rsc
CC=golang-dev
https://golang.org/cl/4532089

src/pkg/mime/multipart/writer.go
src/pkg/mime/multipart/writer_test.go

index 74aa7be1ca7d4d6d791bc0e3a9a4cb4698da708a..1fca73d5ad8fb7cffb126e8612a64647f213c1ff 100644 (file)
@@ -6,24 +6,18 @@ package multipart
 
 import (
        "bytes"
+       "crypto/rand"
        "fmt"
        "io"
        "net/textproto"
        "os"
-       "rand"
        "strings"
 )
 
-// Writer is used to generate multipart messages.
+// A Writer generates multipart messages.
 type Writer struct {
-       // Boundary is the random boundary string between
-       // parts. NewWriter will generate this but it must
-       // not be changed after a part has been created.
-       // Setting this to an invalid value will generate
-       // malformed messages.
-       Boundary string
-
        w        io.Writer
+       boundary string
        lastpart *part
 }
 
@@ -32,38 +26,42 @@ type Writer struct {
 func NewWriter(w io.Writer) *Writer {
        return &Writer{
                w:        w,
-               Boundary: randomBoundary(),
+               boundary: randomBoundary(),
        }
 }
 
+// Boundary returns the Writer's randomly selected boundary string.
+func (w *Writer) Boundary() string {
+       return w.boundary
+}
+
 // FormDataContentType returns the Content-Type for an HTTP
 // multipart/form-data with this Writer's Boundary.
 func (w *Writer) FormDataContentType() string {
-       return "multipart/form-data; boundary=" + w.Boundary
+       return "multipart/form-data; boundary=" + w.boundary
 }
 
-const randChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
-
 func randomBoundary() string {
-       var buf [60]byte
-       for i := range buf {
-               buf[i] = randChars[rand.Intn(len(randChars))]
+       var buf [30]byte
+       _, err := io.ReadFull(rand.Reader, buf[:])
+       if err != nil {
+               panic(err)
        }
-       return string(buf[:])
+       return fmt.Sprintf("%x", buf[:])
 }
 
 // CreatePart creates a new multipart section with the provided
-// header. The previous part, if still open, is closed. The body of
-// the part should be written to the returned WriteCloser. Closing the
-// returned WriteCloser after writing is optional.
-func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.WriteCloser, os.Error) {
+// header. The body of the part should be written to the returned
+// Writer. After calling CreatePart, any previous part may no longer
+// be written to.
+func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, os.Error) {
        if w.lastpart != nil {
-               if err := w.lastpart.Close(); err != nil {
+               if err := w.lastpart.close(); err != nil {
                        return nil, err
                }
        }
        var b bytes.Buffer
-       fmt.Fprintf(&b, "\r\n--%s\r\n", w.Boundary)
+       fmt.Fprintf(&b, "\r\n--%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 {
@@ -91,7 +89,7 @@ func escapeQuotes(s string) string {
 
 // CreateFormFile is a convenience wrapper around CreatePart. It creates
 // a new form-data header with the provided field name and file name.
-func (w *Writer) CreateFormFile(fieldname, filename string) (io.WriteCloser, os.Error) {
+func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, os.Error) {
        h := make(textproto.MIMEHeader)
        h.Set("Content-Disposition",
                fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
@@ -100,40 +98,35 @@ func (w *Writer) CreateFormFile(fieldname, filename string) (io.WriteCloser, os.
        return w.CreatePart(h)
 }
 
-// CreateFormField is a convenience wrapper around CreatePart. It creates
-// a new form-data header with the provided field name.
-func (w *Writer) CreateFormField(fieldname string) (io.WriteCloser, os.Error) {
+// CreateFormField calls calls CreatePart with a header using the
+// given field name.
+func (w *Writer) CreateFormField(fieldname string) (io.Writer, os.Error) {
        h := make(textproto.MIMEHeader)
        h.Set("Content-Disposition",
                fmt.Sprintf(`form-data; name="%s"`, escapeQuotes(fieldname)))
        return w.CreatePart(h)
 }
 
-// WriteField is a convenience wrapper around CreateFormField. It creates and
-// writes a part with the provided name and value.
+// WriteField calls CreateFormField and then writes the given value.
 func (w *Writer) WriteField(fieldname, value string) os.Error {
        p, err := w.CreateFormField(fieldname)
        if err != nil {
                return err
        }
        _, err = p.Write([]byte(value))
-       if err != nil {
-               return err
-       }
-       return p.Close()
+       return err
 }
 
-// Close finishes the multipart message. It closes the previous part,
-// if still open, and writes the trailing boundary end line to the
-// output.
+// Close finishes the multipart message and writes the trailing
+// boundary end line to the output.
 func (w *Writer) Close() os.Error {
        if w.lastpart != nil {
-               if err := w.lastpart.Close(); err != nil {
+               if err := w.lastpart.close(); err != nil {
                        return err
                }
                w.lastpart = nil
        }
-       _, err := fmt.Fprintf(w.w, "\r\n--%s--\r\n", w.Boundary)
+       _, err := fmt.Fprintf(w.w, "\r\n--%s--\r\n", w.boundary)
        return err
 }
 
@@ -143,14 +136,14 @@ type part struct {
        we     os.Error // last error that occurred writing
 }
 
-func (p *part) Close() os.Error {
+func (p *part) close() os.Error {
        p.closed = true
        return p.we
 }
 
 func (p *part) Write(d []byte) (n int, err os.Error) {
        if p.closed {
-               return 0, os.NewError("multipart: Write after Close")
+               return 0, os.NewError("multipart: can't write to finished part")
        }
        n, err = p.mw.w.Write(d)
        if err != nil {
index b85fbf87716d49584ec8fa949fb3fa6f66040162..9935eead39456695a26a69c47331d2c067635792 100644 (file)
@@ -32,7 +32,7 @@ func TestWriter(t *testing.T) {
                }
        }
 
-       r := NewReader(&b, w.Boundary)
+       r := NewReader(&b, w.Boundary())
 
        part, err := r.NextPart()
        if err != nil {