mirror of
https://github.com/golang/go
synced 2024-11-21 16:14:42 -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:
parent
e30b9fd87e
commit
f5181ae9d7
@ -11,6 +11,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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] != '-' {
|
||||
err = ProtocolError("short response: " + line)
|
||||
return
|
||||
@ -224,15 +229,20 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os.
|
||||
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 2
|
||||
// ...
|
||||
// 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
|
||||
// the message line rather than a dash. Each line in message is separated by
|
||||
// a newline (\n).
|
||||
//
|
||||
// where code is a 3-digit status code. The first line starts with the
|
||||
// code and a hyphen. The response is terminated by a line that starts
|
||||
// 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,
|
||||
// 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) {
|
||||
code, continued, message, err := r.readCodeLine(expectCode)
|
||||
for err == nil && continued {
|
||||
line, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var code2 int
|
||||
var moreMessage string
|
||||
code2, continued, moreMessage, err = r.readCodeLine(expectCode)
|
||||
if code != code2 {
|
||||
err = ProtocolError("status code mismatch: " + strconv.Itoa(code) + ", " + strconv.Itoa(code2))
|
||||
code2, continued, moreMessage, err = parseCodeLine(line, expectCode)
|
||||
if err != nil || code2 != code {
|
||||
message += "\n" + strings.TrimRight(line, "\r\n")
|
||||
continued = true
|
||||
continue
|
||||
}
|
||||
message += "\n" + moreMessage
|
||||
}
|
||||
|
@ -138,3 +138,56 @@ func TestReadMIMEHeader(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user