1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
7 Package multipart implements MIME multipart parsing, as defined in RFC
10 The implementation is sufficient for HTTP (RFC 2388) and the multipart
11 bodies generated by popular browsers.
26 // TODO(bradfitz): inline these once the compiler can inline them in
27 // read-only situation (such as bytes.HasSuffix)
29 var crlf = []byte("\r\n")
31 var emptyParams = make(map[string]string)
33 // A Part represents a single part in a multipart body.
35 // The headers of the body, if any, with the keys canonicalized
36 // in the same fashion that the Go http.Request headers are.
37 // i.e. "foo-bar" changes case to "Foo-Bar"
38 Header textproto.MIMEHeader
44 dispositionParams map[string]string
47 // FormName returns the name parameter if p has a Content-Disposition
48 // of type "form-data". Otherwise it returns the empty string.
49 func (p *Part) FormName() string {
50 // See http://tools.ietf.org/html/rfc2183 section 2 for EBNF
51 // of Content-Disposition value format.
52 if p.dispositionParams == nil {
53 p.parseContentDisposition()
55 if p.disposition != "form-data" {
58 return p.dispositionParams["name"]
61 // FileName returns the filename parameter of the Part's
62 // Content-Disposition header.
63 func (p *Part) FileName() string {
64 if p.dispositionParams == nil {
65 p.parseContentDisposition()
67 return p.dispositionParams["filename"]
70 func (p *Part) parseContentDisposition() {
71 v := p.Header.Get("Content-Disposition")
72 p.disposition, p.dispositionParams = mime.ParseMediaType(v)
73 if p.dispositionParams == nil {
74 p.dispositionParams = emptyParams
78 // NewReader creates a new multipart Reader reading from r using the
79 // given MIME boundary.
80 func NewReader(reader io.Reader, boundary string) *Reader {
81 b := []byte("\r\n--" + boundary + "--")
83 bufReader: bufio.NewReader(reader),
86 nlDashBoundary: b[:len(b)-2],
87 dashBoundaryDash: b[2:],
88 dashBoundary: b[2 : len(b)-2],
92 func newPart(mr *Reader) (*Part, os.Error) {
94 Header: make(map[string][]string),
96 buffer: new(bytes.Buffer),
98 if err := bp.populateHeaders(); err != nil {
104 func (bp *Part) populateHeaders() os.Error {
105 r := textproto.NewReader(bp.mr.bufReader)
106 header, err := r.ReadMIMEHeader()
113 // Read reads the body of a part, after its headers and before the
114 // next part (if any) begins.
115 func (bp *Part) Read(p []byte) (n int, err os.Error) {
116 if bp.buffer.Len() >= len(p) {
117 // Internal buffer of unconsumed data is large enough for
118 // the read request. No need to parse more at the moment.
119 return bp.buffer.Read(p)
121 peek, err := bp.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
122 unexpectedEof := err == os.EOF
123 if err != nil && !unexpectedEof {
124 return 0, fmt.Errorf("multipart: Part Read: %v", err)
127 panic("nil peek buf")
130 // Search the peek buffer for "\r\n--boundary". If found,
131 // consume everything up to the boundary. If not, consume only
132 // as much of the peek buffer as cannot hold the boundary
135 foundBoundary := false
136 if idx := bytes.Index(peek, bp.mr.nlDashBoundary); idx != -1 {
139 } else if safeCount := len(peek) - len(bp.mr.nlDashBoundary); safeCount > 0 {
141 } else if unexpectedEof {
142 // If we've run out of peek buffer and the boundary
143 // wasn't found (and can't possibly fit), we must have
144 // hit the end of the file unexpectedly.
145 return 0, io.ErrUnexpectedEOF
148 if _, err := io.Copyn(bp.buffer, bp.mr.bufReader, int64(nCopy)); err != nil {
152 n, err = bp.buffer.Read(p)
153 if err == os.EOF && !foundBoundary {
154 // If the boundary hasn't been reached there's more to
155 // read, so don't pass through an EOF from the buffer
161 func (bp *Part) Close() os.Error {
162 io.Copy(ioutil.Discard, bp)
166 // Reader is an iterator over parts in a MIME multipart body.
167 // Reader's underlying parser consumes its input as needed. Seeking
170 bufReader *bufio.Reader
175 nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
178 // NextPart returns the next part in the multipart or an error.
179 // When there are no more parts, the error os.EOF is returned.
180 func (mr *Reader) NextPart() (*Part, os.Error) {
181 if mr.currentPart != nil {
182 mr.currentPart.Close()
185 expectNewPart := false
187 line, err := mr.bufReader.ReadSlice('\n')
189 return nil, fmt.Errorf("multipart: NextPart: %v", err)
192 if mr.isBoundaryDelimiterLine(line) {
194 bp, err := newPart(mr)
202 if hasPrefixThenNewline(line, mr.dashBoundaryDash) {
208 return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line))
211 if mr.partsRead == 0 {
216 // Consume the "\n" or "\r\n" separator between the
217 // body of the previous part and the boundary line we
218 // now expect will follow. (either a new part or the
220 if bytes.Equal(line, mr.nl) {
225 return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", line)
230 func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
231 // http://tools.ietf.org/html/rfc2046#section-5.1
232 // The boundary delimiter line is then defined as a line
233 // consisting entirely of two hyphen characters ("-",
234 // decimal value 45) followed by the boundary parameter
235 // value from the Content-Type header field, optional linear
236 // whitespace, and a terminating CRLF.
237 if !bytes.HasPrefix(line, mr.dashBoundary) {
240 if bytes.HasSuffix(line, mr.nl) {
241 return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
243 // Violate the spec and also support newlines without the
244 // carriage return...
245 if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
246 if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
248 mr.nlDashBoundary = mr.nlDashBoundary[1:]
255 func onlyHorizontalWhitespace(s []byte) bool {
256 for _, b := range s {
257 if b != ' ' && b != '\t' {
264 func hasPrefixThenNewline(s, prefix []byte) bool {
265 return bytes.HasPrefix(s, prefix) &&
266 (len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
267 len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))