mirror of
https://github.com/golang/go
synced 2024-11-25 10:27:57 -07:00
mime/multipart: parse LF-delimited messages, not just CRLF
Against the spec, but appear in the wild. Fixes #1966 R=golang-dev, adg CC=golang-dev https://golang.org/cl/4662059
This commit is contained in:
parent
52cd055f91
commit
6282292f8d
@ -24,6 +24,11 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(bradfitz): inline these once the compiler can inline them in
|
||||||
|
// read-only situation (such as bytes.HasSuffix)
|
||||||
|
var lf = []byte("\n")
|
||||||
|
var crlf = []byte("\r\n")
|
||||||
|
|
||||||
var headerRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z0-9\\-]+): *([^\r\n]+)")
|
var headerRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z0-9\\-]+): *([^\r\n]+)")
|
||||||
|
|
||||||
var emptyParams = make(map[string]string)
|
var emptyParams = make(map[string]string)
|
||||||
@ -81,6 +86,7 @@ func NewReader(reader io.Reader, boundary string) *Reader {
|
|||||||
return &Reader{
|
return &Reader{
|
||||||
bufReader: bufio.NewReader(reader),
|
bufReader: bufio.NewReader(reader),
|
||||||
|
|
||||||
|
nl: b[:2],
|
||||||
nlDashBoundary: b[:len(b)-2],
|
nlDashBoundary: b[:len(b)-2],
|
||||||
dashBoundaryDash: b[2:],
|
dashBoundaryDash: b[2:],
|
||||||
dashBoundary: b[2 : len(b)-2],
|
dashBoundary: b[2 : len(b)-2],
|
||||||
@ -180,7 +186,7 @@ type Reader struct {
|
|||||||
currentPart *Part
|
currentPart *Part
|
||||||
partsRead int
|
partsRead int
|
||||||
|
|
||||||
nlDashBoundary, dashBoundaryDash, dashBoundary []byte
|
nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextPart returns the next part in the multipart or an error.
|
// NextPart returns the next part in the multipart or an error.
|
||||||
@ -221,11 +227,11 @@ func (mr *Reader) NextPart() (*Part, os.Error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Equal(line, []byte("\r\n")) {
|
// Consume the "\n" or "\r\n" separator between the
|
||||||
// Consume the "\r\n" separator between the
|
// body of the previous part and the boundary line we
|
||||||
// body of the previous part and the boundary
|
// now expect will follow. (either a new part or the
|
||||||
// line we now expect will follow. (either a
|
// end boundary)
|
||||||
// new part or the end boundary)
|
if bytes.Equal(line, mr.nl) {
|
||||||
expectNewPart = true
|
expectNewPart = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -245,13 +251,17 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
|
|||||||
if !bytes.HasPrefix(line, mr.dashBoundary) {
|
if !bytes.HasPrefix(line, mr.dashBoundary) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if bytes.HasSuffix(line, []byte("\r\n")) {
|
if bytes.HasSuffix(line, mr.nl) {
|
||||||
return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-2])
|
return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
|
||||||
}
|
}
|
||||||
// Violate the spec and also support newlines without the
|
// Violate the spec and also support newlines without the
|
||||||
// carriage return...
|
// carriage return...
|
||||||
if bytes.HasSuffix(line, []byte("\n")) {
|
if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
|
||||||
return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1])
|
if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
|
||||||
|
mr.nl = mr.nl[1:]
|
||||||
|
mr.nlDashBoundary = mr.nlDashBoundary[1:]
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -268,5 +278,5 @@ func onlyHorizontalWhitespace(s []byte) bool {
|
|||||||
func hasPrefixThenNewline(s, prefix []byte) bool {
|
func hasPrefixThenNewline(s, prefix []byte) bool {
|
||||||
return bytes.HasPrefix(s, prefix) &&
|
return bytes.HasPrefix(s, prefix) &&
|
||||||
(len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
|
(len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
|
||||||
len(s) == len(prefix)+2 && bytes.HasSuffix(s, []byte("\r\n")))
|
len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ func TestNameAccessors(t *testing.T) {
|
|||||||
|
|
||||||
var longLine = strings.Repeat("\n\n\r\r\r\n\r\000", (1<<20)/8)
|
var longLine = strings.Repeat("\n\n\r\r\r\n\r\000", (1<<20)/8)
|
||||||
|
|
||||||
func testMultipartBody() string {
|
func testMultipartBody(sep string) string {
|
||||||
testBody := `
|
testBody := `
|
||||||
This is a multi-part message. This line is ignored.
|
This is a multi-part message. This line is ignored.
|
||||||
--MyBoundary
|
--MyBoundary
|
||||||
@ -112,21 +112,26 @@ never read data
|
|||||||
|
|
||||||
useless trailer
|
useless trailer
|
||||||
`
|
`
|
||||||
testBody = strings.Replace(testBody, "\n", "\r\n", -1)
|
testBody = strings.Replace(testBody, "\n", sep, -1)
|
||||||
return strings.Replace(testBody, "[longline]", longLine, 1)
|
return strings.Replace(testBody, "[longline]", longLine, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultipart(t *testing.T) {
|
func TestMultipart(t *testing.T) {
|
||||||
bodyReader := strings.NewReader(testMultipartBody())
|
bodyReader := strings.NewReader(testMultipartBody("\r\n"))
|
||||||
testMultipart(t, bodyReader)
|
testMultipart(t, bodyReader, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipartOnlyNewlines(t *testing.T) {
|
||||||
|
bodyReader := strings.NewReader(testMultipartBody("\n"))
|
||||||
|
testMultipart(t, bodyReader, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultipartSlowInput(t *testing.T) {
|
func TestMultipartSlowInput(t *testing.T) {
|
||||||
bodyReader := strings.NewReader(testMultipartBody())
|
bodyReader := strings.NewReader(testMultipartBody("\r\n"))
|
||||||
testMultipart(t, &slowReader{bodyReader})
|
testMultipart(t, &slowReader{bodyReader}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMultipart(t *testing.T, r io.Reader) {
|
func testMultipart(t *testing.T, r io.Reader, onlyNewlines bool) {
|
||||||
reader := NewReader(r, "MyBoundary")
|
reader := NewReader(r, "MyBoundary")
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
@ -149,8 +154,15 @@ func testMultipart(t *testing.T, r io.Reader) {
|
|||||||
if _, err := io.Copy(buf, part); err != nil {
|
if _, err := io.Copy(buf, part); err != nil {
|
||||||
t.Errorf("part 1 copy: %v", err)
|
t.Errorf("part 1 copy: %v", err)
|
||||||
}
|
}
|
||||||
expectEq(t, "My value\r\nThe end.",
|
|
||||||
buf.String(), "Value of first part")
|
adjustNewlines := func(s string) string {
|
||||||
|
if onlyNewlines {
|
||||||
|
return strings.Replace(s, "\r\n", "\n", -1)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
expectEq(t, adjustNewlines("My value\r\nThe end."), buf.String(), "Value of first part")
|
||||||
|
|
||||||
// Part2
|
// Part2
|
||||||
part, err = reader.NextPart()
|
part, err = reader.NextPart()
|
||||||
@ -187,7 +199,7 @@ func testMultipart(t *testing.T, r io.Reader) {
|
|||||||
if _, err := io.Copy(buf, part); err != nil {
|
if _, err := io.Copy(buf, part); err != nil {
|
||||||
t.Errorf("part 3 copy: %v", err)
|
t.Errorf("part 3 copy: %v", err)
|
||||||
}
|
}
|
||||||
expectEq(t, "Line 1\r\nLine 2\r\nLine 3 ends in a newline, but just one.\r\n",
|
expectEq(t, adjustNewlines("Line 1\r\nLine 2\r\nLine 3 ends in a newline, but just one.\r\n"),
|
||||||
buf.String(), "body of part 3")
|
buf.String(), "body of part 3")
|
||||||
|
|
||||||
// Part4
|
// Part4
|
||||||
|
Loading…
Reference in New Issue
Block a user