113
109
// Read reads the body of a part, after its headers and before the
114
110
// next part (if any) begins.
115
111
func (p *Part) Read(d []byte) (n int, err error) {
116
115
if p.buffer.Len() >= len(d) {
117
116
// Internal buffer of unconsumed data is large enough for
118
117
// the read request. No need to parse more at the moment.
119
118
return p.buffer.Read(d)
121
120
peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
122
unexpectedEof := err == io.EOF
123
if err != nil && !unexpectedEof {
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 {
124
132
return 0, fmt.Errorf("multipart: Part Read: %v", err)
172
180
currentPart *Part
175
nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
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"
178
189
// NextPart returns the next part in the multipart or an error.
185
196
expectNewPart := false
187
198
line, err := r.bufReader.ReadSlice('\n')
188
if err == io.EOF && bytes.Equal(line, r.dashBoundaryDash) {
199
if err == io.EOF && r.isFinalBoundary(line) {
189
200
// If the buffer ends in "--boundary--" without the
190
201
// trailing "\r\n", ReadSlice will return an error
191
202
// (since it's missing the '\n'), but this is a valid
235
246
panic("unreachable")
238
func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
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) {
239
262
// http://tools.ietf.org/html/rfc2046#section-5.1
240
263
// The boundary delimiter line is then defined as a line
241
264
// consisting entirely of two hyphen characters ("-",
245
268
if !bytes.HasPrefix(line, mr.dashBoundary) {
248
if bytes.HasSuffix(line, mr.nl) {
249
return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
251
// Violate the spec and also support newlines without the
252
// carriage return...
253
if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
254
if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
256
mr.nlDashBoundary = mr.nlDashBoundary[1:]
263
func onlyHorizontalWhitespace(s []byte) bool {
264
for _, b := range s {
265
if b != ' ' && b != '\t' {
272
func hasPrefixThenNewline(s, prefix []byte) bool {
273
return bytes.HasPrefix(s, prefix) &&
274
(len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
275
len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
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') {