Commit e955a3cc authored by Andrew Gerrand's avatar Andrew Gerrand

net/textproto: always copy the data from bufio to avoid corruption

Fixes #2621.

R=rsc, rsc
CC=golang-dev
https://golang.org/cl/5498104
parent 610757b1
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
type Reader struct { type Reader struct {
R *bufio.Reader R *bufio.Reader
dot *dotReader dot *dotReader
buf []byte // a re-usable buffer for readContinuedLineSlice
} }
// NewReader returns a new Reader reading from r. // NewReader returns a new Reader reading from r.
...@@ -121,74 +122,44 @@ func (r *Reader) readContinuedLineSlice() ([]byte, error) { ...@@ -121,74 +122,44 @@ func (r *Reader) readContinuedLineSlice() ([]byte, error) {
// Read the first line. // Read the first line.
line, err := r.readLineSlice() line, err := r.readLineSlice()
if err != nil { if err != nil {
return line, err return nil, err
} }
if len(line) == 0 { // blank line - no continuation if len(line) == 0 { // blank line - no continuation
return line, nil return line, nil
} }
line = trim(line)
copied := false // ReadByte or the next readLineSlice will flush the read buffer;
if r.R.Buffered() < 1 { // copy the slice into buf.
// ReadByte will flush the buffer; make a copy of the slice. r.buf = append(r.buf[:0], trim(line)...)
copied = true
line = append([]byte(nil), line...)
}
// Look for a continuation line.
c, err := r.R.ReadByte()
if err != nil {
// Delay err until we read the byte next time.
return line, nil
}
if c != ' ' && c != '\t' {
// Not a continuation.
r.R.UnreadByte()
return line, nil
}
if !copied {
// The next readLineSlice will invalidate the previous one.
line = append(make([]byte, 0, len(line)*2), line...)
}
// Read continuation lines. // Read continuation lines.
for { for r.skipSpace() > 0 {
// Consume leading spaces; one already gone. line, err := r.readLineSlice()
for {
c, err = r.R.ReadByte()
if err != nil {
break
}
if c != ' ' && c != '\t' {
r.R.UnreadByte()
break
}
}
var cont []byte
cont, err = r.readLineSlice()
cont = trim(cont)
line = append(line, ' ')
line = append(line, cont...)
if err != nil { if err != nil {
break break
} }
r.buf = append(r.buf, ' ')
r.buf = append(r.buf, line...)
}
return r.buf, nil
}
// Check for leading space on next line. // skipSpace skips R over all spaces and returns the number of bytes skipped.
if c, err = r.R.ReadByte(); err != nil { func (r *Reader) skipSpace() int {
n := 0
for {
c, err := r.R.ReadByte()
if err != nil {
// Bufio will keep err until next read.
break break
} }
if c != ' ' && c != '\t' { if c != ' ' && c != '\t' {
r.R.UnreadByte() r.R.UnreadByte()
break break
} }
n++
} }
return n
// Delay error until next call.
if len(line) > 0 {
err = nil
}
return line, err
} }
func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err error) { func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err error) {
......
...@@ -138,6 +138,15 @@ func TestReadMIMEHeader(t *testing.T) { ...@@ -138,6 +138,15 @@ func TestReadMIMEHeader(t *testing.T) {
} }
} }
func TestReadMIMEHeaderSingle(t *testing.T) {
r := reader("Foo: bar\n\n")
m, err := r.ReadMIMEHeader()
want := MIMEHeader{"Foo": {"bar"}}
if !reflect.DeepEqual(m, want) || err != nil {
t.Fatalf("ReadMIMEHeader: %v, %v; want %v", m, err, want)
}
}
func TestLargeReadMIMEHeader(t *testing.T) { func TestLargeReadMIMEHeader(t *testing.T) {
data := make([]byte, 16*1024) data := make([]byte, 16*1024)
for i := 0; i < len(data); i++ { for i := 0; i < len(data); i++ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment