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.
25 var emptyParams = make(map[string]string)
27 // A Part represents a single part in a multipart body.
29 // The headers of the body, if any, with the keys canonicalized
30 // in the same fashion that the Go http.Request headers are.
31 // i.e. "foo-bar" changes case to "Foo-Bar"
32 Header textproto.MIMEHeader
39 dispositionParams map[string]string
42 // FormName returns the name parameter if p has a Content-Disposition
43 // of type "form-data". Otherwise it returns the empty string.
44 func (p *Part) FormName() string {
45 // See http://tools.ietf.org/html/rfc2183 section 2 for EBNF
46 // of Content-Disposition value format.
47 if p.dispositionParams == nil {
48 p.parseContentDisposition()
50 if p.disposition != "form-data" {
53 return p.dispositionParams["name"]
56 // FileName returns the filename parameter of the Part's
57 // Content-Disposition header.
58 func (p *Part) FileName() string {
59 if p.dispositionParams == nil {
60 p.parseContentDisposition()
62 return p.dispositionParams["filename"]
65 func (p *Part) parseContentDisposition() {
66 v := p.Header.Get("Content-Disposition")
68 p.disposition, p.dispositionParams, err = mime.ParseMediaType(v)
70 p.dispositionParams = emptyParams
74 // NewReader creates a new multipart Reader reading from reader using the
75 // given MIME boundary.
76 func NewReader(reader io.Reader, boundary string) *Reader {
77 b := []byte("\r\n--" + boundary + "--")
79 bufReader: bufio.NewReader(reader),
82 nlDashBoundary: b[:len(b)-2],
83 dashBoundaryDash: b[2:],
84 dashBoundary: b[2 : len(b)-2],
88 func newPart(mr *Reader) (*Part, error) {
90 Header: make(map[string][]string),
92 buffer: new(bytes.Buffer),
94 if err := bp.populateHeaders(); err != nil {
100 func (bp *Part) populateHeaders() error {
101 r := textproto.NewReader(bp.mr.bufReader)
102 header, err := r.ReadMIMEHeader()
109 // Read reads the body of a part, after its headers and before the
110 // next part (if any) begins.
111 func (p *Part) Read(d []byte) (n int, err error) {
115 if p.buffer.Len() >= len(d) {
116 // Internal buffer of unconsumed data is large enough for
117 // the read request. No need to parse more at the moment.
118 return p.buffer.Read(d)
120 peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
122 // Look for an immediate empty part without a leading \r\n
123 // before the boundary separator. Some MIME code makes empty
124 // parts like this. Most browsers, however, write the \r\n
125 // before the subsequent boundary even for empty parts and
126 // won't hit this path.
127 if p.bytesRead == 0 && p.mr.peekBufferIsEmptyPart(peek) {
130 unexpectedEOF := err == io.EOF
131 if err != nil && !unexpectedEOF {
132 return 0, fmt.Errorf("multipart: Part Read: %v", err)
135 panic("nil peek buf")
138 // Search the peek buffer for "\r\n--boundary". If found,
139 // consume everything up to the boundary. If not, consume only
140 // as much of the peek buffer as cannot hold the boundary
143 foundBoundary := false
144 if idx := bytes.Index(peek, p.mr.nlDashBoundary); idx != -1 {
147 } else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
149 } else if unexpectedEOF {
150 // If we've run out of peek buffer and the boundary
151 // wasn't found (and can't possibly fit), we must have
152 // hit the end of the file unexpectedly.
153 return 0, io.ErrUnexpectedEOF
156 if _, err := io.CopyN(p.buffer, p.mr.bufReader, int64(nCopy)); err != nil {
160 n, err = p.buffer.Read(d)
161 if err == io.EOF && !foundBoundary {
162 // If the boundary hasn't been reached there's more to
163 // read, so don't pass through an EOF from the buffer
169 func (p *Part) Close() error {
170 io.Copy(ioutil.Discard, p)
174 // Reader is an iterator over parts in a MIME multipart body.
175 // Reader's underlying parser consumes its input as needed. Seeking
178 bufReader *bufio.Reader
183 nl []byte // "\r\n" or "\n" (set after seeing first boundary line)
184 nlDashBoundary []byte // nl + "--boundary"
185 dashBoundaryDash []byte // "--boundary--"
186 dashBoundary []byte // "--boundary"
189 // NextPart returns the next part in the multipart or an error.
190 // When there are no more parts, the error io.EOF is returned.
191 func (r *Reader) NextPart() (*Part, error) {
192 if r.currentPart != nil {
193 r.currentPart.Close()
196 expectNewPart := false
198 line, err := r.bufReader.ReadSlice('\n')
199 if err == io.EOF && r.isFinalBoundary(line) {
200 // If the buffer ends in "--boundary--" without the
201 // trailing "\r\n", ReadSlice will return an error
202 // (since it's missing the '\n'), but this is a valid
203 // multipart EOF so we need to return io.EOF instead of
204 // a fmt-wrapped one.
208 return nil, fmt.Errorf("multipart: NextPart: %v", err)
211 if r.isBoundaryDelimiterLine(line) {
213 bp, err := newPart(r)
221 if r.isFinalBoundary(line) {
227 return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line))
230 if r.partsRead == 0 {
235 // Consume the "\n" or "\r\n" separator between the
236 // body of the previous part and the boundary line we
237 // now expect will follow. (either a new part or the
239 if bytes.Equal(line, r.nl) {
244 return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", line)
249 // isFinalBoundary returns whether line is the final boundary line
250 // indiciating that all parts are over.
251 // It matches `^--boundary--[ \t]*(\r\n)?$`
252 func (mr *Reader) isFinalBoundary(line []byte) bool {
253 if !bytes.HasPrefix(line, mr.dashBoundaryDash) {
256 rest := line[len(mr.dashBoundaryDash):]
257 rest = skipLWSPChar(rest)
258 return len(rest) == 0 || bytes.Equal(rest, mr.nl)
261 func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) {
262 // http://tools.ietf.org/html/rfc2046#section-5.1
263 // The boundary delimiter line is then defined as a line
264 // consisting entirely of two hyphen characters ("-",
265 // decimal value 45) followed by the boundary parameter
266 // value from the Content-Type header field, optional linear
267 // whitespace, and a terminating CRLF.
268 if !bytes.HasPrefix(line, mr.dashBoundary) {
271 rest := line[len(mr.dashBoundary):]
272 rest = skipLWSPChar(rest)
274 // On the first part, see our lines are ending in \n instead of \r\n
275 // and switch into that mode if so. This is a violation of the spec,
276 // but occurs in practice.
277 if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' {
279 mr.nlDashBoundary = mr.nlDashBoundary[1:]
281 return bytes.Equal(rest, mr.nl)
284 // peekBufferIsEmptyPart returns whether the provided peek-ahead
285 // buffer represents an empty part. This is only called if we've not
286 // already read any bytes in this part and checks for the case of MIME
287 // software not writing the \r\n on empty parts. Some does, some
290 // This checks that what follows the "--boundary" is actually the end
291 // ("--boundary--" with optional whitespace) or optional whitespace
292 // and then a newline, so we don't catch "--boundaryFAKE", in which
293 // case the whole line is part of the data.
294 func (mr *Reader) peekBufferIsEmptyPart(peek []byte) bool {
295 // End of parts case.
296 // Test whether peek matches `^--boundary--[ \t]*(?:\r\n|$)`
297 if bytes.HasPrefix(peek, mr.dashBoundaryDash) {
298 rest := peek[len(mr.dashBoundaryDash):]
299 rest = skipLWSPChar(rest)
300 return bytes.HasPrefix(rest, mr.nl) || len(rest) == 0
302 if !bytes.HasPrefix(peek, mr.dashBoundary) {
305 // Test whether rest matches `^[ \t]*\r\n`)
306 rest := peek[len(mr.dashBoundary):]
307 rest = skipLWSPChar(rest)
308 return bytes.HasPrefix(rest, mr.nl)
311 // skipLWSPChar returns b with leading spaces and tabs removed.
313 // LWSP-char = SPACE / HTAB
314 func skipLWSPChar(b []byte) []byte {
315 for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {