1
0
mirror of https://github.com/golang/go synced 2024-11-25 04:27:56 -07:00

mime/multipart: misc code/doc fixes

R=rsc
CC=golang-dev
https://golang.org/cl/4532089
This commit is contained in:
Brad Fitzpatrick 2011-05-25 19:21:05 -07:00
parent 0836b86e8b
commit 4923ba9155
2 changed files with 34 additions and 41 deletions

View File

@ -6,24 +6,18 @@ package multipart
import ( import (
"bytes" "bytes"
"crypto/rand"
"fmt" "fmt"
"io" "io"
"net/textproto" "net/textproto"
"os" "os"
"rand"
"strings" "strings"
) )
// Writer is used to generate multipart messages. // A Writer generates multipart messages.
type Writer struct { 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 w io.Writer
boundary string
lastpart *part lastpart *part
} }
@ -32,38 +26,42 @@ type Writer struct {
func NewWriter(w io.Writer) *Writer { func NewWriter(w io.Writer) *Writer {
return &Writer{ return &Writer{
w: w, 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 // FormDataContentType returns the Content-Type for an HTTP
// multipart/form-data with this Writer's Boundary. // multipart/form-data with this Writer's Boundary.
func (w *Writer) FormDataContentType() string { 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 { func randomBoundary() string {
var buf [60]byte var buf [30]byte
for i := range buf { _, err := io.ReadFull(rand.Reader, buf[:])
buf[i] = randChars[rand.Intn(len(randChars))] if err != nil {
panic(err)
} }
return string(buf[:]) return fmt.Sprintf("%x", buf[:])
} }
// CreatePart creates a new multipart section with the provided // CreatePart creates a new multipart section with the provided
// header. The previous part, if still open, is closed. The body of // header. The body of the part should be written to the returned
// the part should be written to the returned WriteCloser. Closing the // Writer. After calling CreatePart, any previous part may no longer
// returned WriteCloser after writing is optional. // be written to.
func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.WriteCloser, os.Error) { func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, os.Error) {
if w.lastpart != nil { if w.lastpart != nil {
if err := w.lastpart.Close(); err != nil { if err := w.lastpart.close(); err != nil {
return nil, err return nil, err
} }
} }
var b bytes.Buffer 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 // TODO(bradfitz): move this to textproto.MimeHeader.Write(w), have it sort
// and clean, like http.Header.Write(w) does. // and clean, like http.Header.Write(w) does.
for k, vv := range header { for k, vv := range header {
@ -91,7 +89,7 @@ func escapeQuotes(s string) string {
// CreateFormFile is a convenience wrapper around CreatePart. It creates // CreateFormFile is a convenience wrapper around CreatePart. It creates
// a new form-data header with the provided field name and file name. // 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 := make(textproto.MIMEHeader)
h.Set("Content-Disposition", h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"; filename="%s"`, 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) return w.CreatePart(h)
} }
// CreateFormField is a convenience wrapper around CreatePart. It creates // CreateFormField calls calls CreatePart with a header using the
// a new form-data header with the provided field name. // given field name.
func (w *Writer) CreateFormField(fieldname string) (io.WriteCloser, os.Error) { func (w *Writer) CreateFormField(fieldname string) (io.Writer, os.Error) {
h := make(textproto.MIMEHeader) h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"`, escapeQuotes(fieldname))) fmt.Sprintf(`form-data; name="%s"`, escapeQuotes(fieldname)))
return w.CreatePart(h) return w.CreatePart(h)
} }
// WriteField is a convenience wrapper around CreateFormField. It creates and // WriteField calls CreateFormField and then writes the given value.
// writes a part with the provided name and value.
func (w *Writer) WriteField(fieldname, value string) os.Error { func (w *Writer) WriteField(fieldname, value string) os.Error {
p, err := w.CreateFormField(fieldname) p, err := w.CreateFormField(fieldname)
if err != nil { if err != nil {
return err return err
} }
_, err = p.Write([]byte(value)) _, err = p.Write([]byte(value))
if err != nil {
return err return err
}
return p.Close()
} }
// Close finishes the multipart message. It closes the previous part, // Close finishes the multipart message and writes the trailing
// if still open, and writes the trailing boundary end line to the // boundary end line to the output.
// output.
func (w *Writer) Close() os.Error { func (w *Writer) Close() os.Error {
if w.lastpart != nil { if w.lastpart != nil {
if err := w.lastpart.Close(); err != nil { if err := w.lastpart.close(); err != nil {
return err return err
} }
w.lastpart = nil 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 return err
} }
@ -143,14 +136,14 @@ type part struct {
we os.Error // last error that occurred writing we os.Error // last error that occurred writing
} }
func (p *part) Close() os.Error { func (p *part) close() os.Error {
p.closed = true p.closed = true
return p.we return p.we
} }
func (p *part) Write(d []byte) (n int, err os.Error) { func (p *part) Write(d []byte) (n int, err os.Error) {
if p.closed { 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) n, err = p.mw.w.Write(d)
if err != nil { if err != nil {

View File

@ -32,7 +32,7 @@ func TestWriter(t *testing.T) {
} }
} }
r := NewReader(&b, w.Boundary) r := NewReader(&b, w.Boundary())
part, err := r.NextPart() part, err := r.NextPart()
if err != nil { if err != nil {