1
0
mirror of https://github.com/golang/go synced 2024-11-22 03:54:39 -07:00

textproto: parse RFC 959 multiline responses correctly

Fixes #2218

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5037041
This commit is contained in:
Brad Fitzpatrick 2011-09-15 14:29:59 -07:00
parent e30b9fd87e
commit f5181ae9d7
2 changed files with 78 additions and 8 deletions

View File

@ -11,6 +11,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"strconv" "strconv"
"strings"
) )
// BUG(rsc): To let callers manage exposure to denial of service // BUG(rsc): To let callers manage exposure to denial of service
@ -182,6 +183,10 @@ func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message
if err != nil { if err != nil {
return return
} }
return parseCodeLine(line, expectCode)
}
func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err os.Error) {
if len(line) < 4 || line[3] != ' ' && line[3] != '-' { if len(line) < 4 || line[3] != ' ' && line[3] != '-' {
err = ProtocolError("short response: " + line) err = ProtocolError("short response: " + line)
return return
@ -224,15 +229,20 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os.
return return
} }
// ReadResponse reads a multi-line response of the form // ReadResponse reads a multi-line response of the form:
//
// code-message line 1 // code-message line 1
// code-message line 2 // code-message line 2
// ... // ...
// code message line n // code message line n
// where code is a 3-digit status code. Each line should have the same code. //
// The response is terminated by a line that uses a space between the code and // where code is a 3-digit status code. The first line starts with the
// the message line rather than a dash. Each line in message is separated by // code and a hyphen. The response is terminated by a line that starts
// a newline (\n). // with the same code followed by a space. Each line in message is
// separated by a newline (\n).
//
// See page 36 of RFC 959 (http://www.ietf.org/rfc/rfc959.txt) for
// details.
// //
// If the prefix of the status does not match the digits in expectCode, // If the prefix of the status does not match the digits in expectCode,
// ReadResponse returns with err set to &Error{code, message}. // ReadResponse returns with err set to &Error{code, message}.
@ -244,11 +254,18 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os.
func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os.Error) { func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os.Error) {
code, continued, message, err := r.readCodeLine(expectCode) code, continued, message, err := r.readCodeLine(expectCode)
for err == nil && continued { for err == nil && continued {
line, err := r.ReadLine()
if err != nil {
return
}
var code2 int var code2 int
var moreMessage string var moreMessage string
code2, continued, moreMessage, err = r.readCodeLine(expectCode) code2, continued, moreMessage, err = parseCodeLine(line, expectCode)
if code != code2 { if err != nil || code2 != code {
err = ProtocolError("status code mismatch: " + strconv.Itoa(code) + ", " + strconv.Itoa(code2)) message += "\n" + strings.TrimRight(line, "\r\n")
continued = true
continue
} }
message += "\n" + moreMessage message += "\n" + moreMessage
} }

View File

@ -138,3 +138,56 @@ func TestReadMIMEHeader(t *testing.T) {
t.Fatalf("ReadMIMEHeader: %v, %v; want %v", m, err, want) t.Fatalf("ReadMIMEHeader: %v, %v; want %v", m, err, want)
} }
} }
type readResponseTest struct {
in string
inCode int
wantCode int
wantMsg string
}
var readResponseTests = []readResponseTest{
{"230-Anonymous access granted, restrictions apply\n" +
"Read the file README.txt,\n" +
"230 please",
23,
230,
"Anonymous access granted, restrictions apply\nRead the file README.txt,\n please",
},
{"230 Anonymous access granted, restrictions apply\n",
23,
230,
"Anonymous access granted, restrictions apply",
},
{"400-A\n400-B\n400 C",
4,
400,
"A\nB\nC",
},
{"400-A\r\n400-B\r\n400 C\r\n",
4,
400,
"A\nB\nC",
},
}
// See http://www.ietf.org/rfc/rfc959.txt page 36.
func TestRFC959Lines(t *testing.T) {
for i, tt := range readResponseTests {
r := reader(tt.in + "\nFOLLOWING DATA")
code, msg, err := r.ReadResponse(tt.inCode)
if err != nil {
t.Errorf("#%d: ReadResponse: %v", i, err)
continue
}
if code != tt.wantCode {
t.Errorf("#%d: code=%d, want %d", i, code, tt.wantCode)
}
if msg != tt.wantMsg {
t.Errorf("%#d: msg=%q, want %q", i, msg, tt.wantMsg)
}
}
}