diff options
author | Ondřej Surý <ondrej@sury.org> | 2012-01-30 15:38:19 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2012-01-30 15:38:19 +0100 |
commit | 4cecda6c347bd6902b960c6a35a967add7070b0d (patch) | |
tree | a462e224ff41ec9f3eb1a0b6e815806f9e8804ad /src/pkg/net/textproto | |
parent | 6c7ca6e4d4e26e4c8cbe0d183966011b3b088a0a (diff) | |
download | golang-4cecda6c347bd6902b960c6a35a967add7070b0d.tar.gz |
Imported Upstream version 2012.01.27upstream-weekly/2012.01.27
Diffstat (limited to 'src/pkg/net/textproto')
-rw-r--r-- | src/pkg/net/textproto/header.go | 2 | ||||
-rw-r--r-- | src/pkg/net/textproto/pipeline.go | 2 | ||||
-rw-r--r-- | src/pkg/net/textproto/reader.go | 130 | ||||
-rw-r--r-- | src/pkg/net/textproto/reader_test.go | 35 | ||||
-rw-r--r-- | src/pkg/net/textproto/textproto.go | 11 | ||||
-rw-r--r-- | src/pkg/net/textproto/writer.go | 7 |
6 files changed, 97 insertions, 90 deletions
diff --git a/src/pkg/net/textproto/header.go b/src/pkg/net/textproto/header.go index 288deb2ce..7fb32f804 100644 --- a/src/pkg/net/textproto/header.go +++ b/src/pkg/net/textproto/header.go @@ -39,5 +39,5 @@ func (h MIMEHeader) Get(key string) string { // Del deletes the values associated with key. func (h MIMEHeader) Del(key string) { - h[CanonicalMIMEHeaderKey(key)] = nil, false + delete(h, CanonicalMIMEHeaderKey(key)) } diff --git a/src/pkg/net/textproto/pipeline.go b/src/pkg/net/textproto/pipeline.go index 8c25884b3..ca50eddac 100644 --- a/src/pkg/net/textproto/pipeline.go +++ b/src/pkg/net/textproto/pipeline.go @@ -108,7 +108,7 @@ func (s *sequencer) End(id uint) { } c, ok := s.wait[id] if ok { - s.wait[id] = nil, false + delete(s.wait, id) } s.mu.Unlock() if ok { diff --git a/src/pkg/net/textproto/reader.go b/src/pkg/net/textproto/reader.go index a404f4758..862cd536c 100644 --- a/src/pkg/net/textproto/reader.go +++ b/src/pkg/net/textproto/reader.go @@ -9,7 +9,6 @@ import ( "bytes" "io" "io/ioutil" - "os" "strconv" "strings" ) @@ -23,6 +22,7 @@ import ( type Reader struct { R *bufio.Reader dot *dotReader + buf []byte // a re-usable buffer for readContinuedLineSlice } // NewReader returns a new Reader reading from r. @@ -32,13 +32,13 @@ func NewReader(r *bufio.Reader) *Reader { // ReadLine reads a single line from r, // eliding the final \n or \r\n from the returned string. -func (r *Reader) ReadLine() (string, os.Error) { +func (r *Reader) ReadLine() (string, error) { line, err := r.readLineSlice() return string(line), err } // ReadLineBytes is like ReadLine but returns a []byte instead of a string. -func (r *Reader) ReadLineBytes() ([]byte, os.Error) { +func (r *Reader) ReadLineBytes() ([]byte, error) { line, err := r.readLineSlice() if line != nil { buf := make([]byte, len(line)) @@ -48,10 +48,24 @@ func (r *Reader) ReadLineBytes() ([]byte, os.Error) { return line, err } -func (r *Reader) readLineSlice() ([]byte, os.Error) { +func (r *Reader) readLineSlice() ([]byte, error) { r.closeDot() - line, _, err := r.R.ReadLine() - return line, err + var line []byte + for { + l, more, err := r.R.ReadLine() + if err != nil { + return nil, err + } + // Avoid the copy if the first call produced a full line. + if line == nil && !more { + return l, nil + } + line = append(line, l...) + if !more { + break + } + } + return line, nil } // ReadContinuedLine reads a possibly continued line from r, @@ -73,7 +87,7 @@ func (r *Reader) readLineSlice() ([]byte, os.Error) { // // A line consisting of only white space is never continued. // -func (r *Reader) ReadContinuedLine() (string, os.Error) { +func (r *Reader) ReadContinuedLine() (string, error) { line, err := r.readContinuedLineSlice() return string(line), err } @@ -94,7 +108,7 @@ func trim(s []byte) []byte { // ReadContinuedLineBytes is like ReadContinuedLine but // returns a []byte instead of a string. -func (r *Reader) ReadContinuedLineBytes() ([]byte, os.Error) { +func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { line, err := r.readContinuedLineSlice() if line != nil { buf := make([]byte, len(line)) @@ -104,81 +118,51 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, os.Error) { return line, err } -func (r *Reader) readContinuedLineSlice() ([]byte, os.Error) { +func (r *Reader) readContinuedLineSlice() ([]byte, error) { // Read the first line. line, err := r.readLineSlice() if err != nil { - return line, err + return nil, err } if len(line) == 0 { // blank line - no continuation return line, nil } - line = trim(line) - copied := false - if r.R.Buffered() < 1 { - // ReadByte will flush the buffer; make a copy of the slice. - 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...) - } + // ReadByte or the next readLineSlice will flush the read buffer; + // copy the slice into buf. + r.buf = append(r.buf[:0], trim(line)...) // Read continuation lines. - for { - // Consume leading spaces; one already gone. - 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...) + for r.skipSpace() > 0 { + line, err := r.readLineSlice() if err != nil { break } + r.buf = append(r.buf, ' ') + r.buf = append(r.buf, line...) + } + return r.buf, nil +} - // Check for leading space on next line. - if c, err = r.R.ReadByte(); err != nil { +// skipSpace skips R over all spaces and returns the number of bytes skipped. +func (r *Reader) skipSpace() int { + n := 0 + for { + c, err := r.R.ReadByte() + if err != nil { + // Bufio will keep err until next read. break } if c != ' ' && c != '\t' { r.R.UnreadByte() break } + n++ } - - // Delay error until next call. - if len(line) > 0 { - err = nil - } - return line, err + return n } -func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err os.Error) { +func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err error) { line, err := r.ReadLine() if err != nil { return @@ -186,7 +170,7 @@ func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message return parseCodeLine(line, expectCode) } -func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err os.Error) { +func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err error) { if len(line) < 4 || line[3] != ' ' && line[3] != '-' { err = ProtocolError("short response: " + line) return @@ -221,7 +205,7 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa // // An expectCode <= 0 disables the check of the status code. // -func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os.Error) { +func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) { code, continued, message, err := r.readCodeLine(expectCode) if err == nil && continued { err = ProtocolError("unexpected multi-line response: " + message) @@ -251,12 +235,12 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os. // // An expectCode <= 0 disables the check of the status code. // -func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os.Error) { +func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) { code, continued, message, err := r.readCodeLine(expectCode) for err == nil && continued { line, err := r.ReadLine() if err != nil { - return + return 0, "", err } var code2 int @@ -286,7 +270,7 @@ func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os. // // The decoded form returned by the Reader's Read method // rewrites the "\r\n" line endings into the simpler "\n", -// removes leading dot escapes if present, and stops with error os.EOF +// removes leading dot escapes if present, and stops with error io.EOF // after consuming (and discarding) the end-of-sequence line. func (r *Reader) DotReader() io.Reader { r.closeDot() @@ -300,7 +284,7 @@ type dotReader struct { } // Read satisfies reads by decoding dot-encoded data read from d.r. -func (d *dotReader) Read(b []byte) (n int, err os.Error) { +func (d *dotReader) Read(b []byte) (n int, err error) { // Run data through a simple state machine to // elide leading dots, rewrite trailing \r\n into \n, // and detect ending .\r\n line. @@ -317,7 +301,7 @@ func (d *dotReader) Read(b []byte) (n int, err os.Error) { var c byte c, err = br.ReadByte() if err != nil { - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } break @@ -379,7 +363,7 @@ func (d *dotReader) Read(b []byte) (n int, err os.Error) { n++ } if err == nil && d.state == stateEOF { - err = os.EOF + err = io.EOF } if err != nil && d.r.dot == d { d.r.dot = nil @@ -404,7 +388,7 @@ func (r *Reader) closeDot() { // ReadDotBytes reads a dot-encoding and returns the decoded data. // // See the documentation for the DotReader method for details about dot-encoding. -func (r *Reader) ReadDotBytes() ([]byte, os.Error) { +func (r *Reader) ReadDotBytes() ([]byte, error) { return ioutil.ReadAll(r.DotReader()) } @@ -412,17 +396,17 @@ func (r *Reader) ReadDotBytes() ([]byte, os.Error) { // containing the decoded lines, with the final \r\n or \n elided from each. // // See the documentation for the DotReader method for details about dot-encoding. -func (r *Reader) ReadDotLines() ([]string, os.Error) { +func (r *Reader) ReadDotLines() ([]string, error) { // We could use ReadDotBytes and then Split it, // but reading a line at a time avoids needing a // large contiguous block of memory and is simpler. var v []string - var err os.Error + var err error for { var line string line, err = r.ReadLine() if err != nil { - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } break @@ -460,7 +444,7 @@ func (r *Reader) ReadDotLines() ([]string, os.Error) { // "Long-Key": {"Even Longer Value"}, // } // -func (r *Reader) ReadMIMEHeader() (MIMEHeader, os.Error) { +func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { m := make(MIMEHeader) for { kv, err := r.readContinuedLineSlice() diff --git a/src/pkg/net/textproto/reader_test.go b/src/pkg/net/textproto/reader_test.go index 23ebc3f61..4d0369148 100644 --- a/src/pkg/net/textproto/reader_test.go +++ b/src/pkg/net/textproto/reader_test.go @@ -7,7 +7,6 @@ package textproto import ( "bufio" "io" - "os" "reflect" "strings" "testing" @@ -49,7 +48,7 @@ func TestReadLine(t *testing.T) { t.Fatalf("Line 2: %s, %v", s, err) } s, err = r.ReadLine() - if s != "" || err != os.EOF { + if s != "" || err != io.EOF { t.Fatalf("EOF: %s, %v", s, err) } } @@ -69,7 +68,7 @@ func TestReadContinuedLine(t *testing.T) { t.Fatalf("Line 3: %s, %v", s, err) } s, err = r.ReadContinuedLine() - if s != "" || err != os.EOF { + if s != "" || err != io.EOF { t.Fatalf("EOF: %s, %v", s, err) } } @@ -92,7 +91,7 @@ func TestReadCodeLine(t *testing.T) { t.Fatalf("Line 3: wrong error %v\n", err) } code, msg, err = r.ReadCodeLine(1) - if code != 0 || msg != "" || err != os.EOF { + if code != 0 || msg != "" || err != io.EOF { t.Fatalf("EOF: %d, %s, %v", code, msg, err) } } @@ -139,6 +138,32 @@ 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) { + data := make([]byte, 16*1024) + for i := 0; i < len(data); i++ { + data[i] = 'x' + } + sdata := string(data) + r := reader("Cookie: " + sdata + "\r\n\n") + m, err := r.ReadMIMEHeader() + if err != nil { + t.Fatalf("ReadMIMEHeader: %v", err) + } + cookie := m.Get("Cookie") + if cookie != sdata { + t.Fatalf("ReadMIMEHeader: %v bytes, want %v bytes", len(cookie), len(sdata)) + } +} + type readResponseTest struct { in string inCode int @@ -187,7 +212,7 @@ func TestRFC959Lines(t *testing.T) { 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) + t.Errorf("#%d: msg=%q, want %q", i, msg, tt.wantMsg) } } } diff --git a/src/pkg/net/textproto/textproto.go b/src/pkg/net/textproto/textproto.go index 9f19b5495..317ec72b0 100644 --- a/src/pkg/net/textproto/textproto.go +++ b/src/pkg/net/textproto/textproto.go @@ -27,7 +27,6 @@ import ( "fmt" "io" "net" - "os" ) // An Error represents a numeric error response from a server. @@ -36,7 +35,7 @@ type Error struct { Msg string } -func (e *Error) String() string { +func (e *Error) Error() string { return fmt.Sprintf("%03d %s", e.Code, e.Msg) } @@ -44,7 +43,7 @@ func (e *Error) String() string { // as an invalid response or a hung-up connection. type ProtocolError string -func (p ProtocolError) String() string { +func (p ProtocolError) Error() string { return string(p) } @@ -70,13 +69,13 @@ func NewConn(conn io.ReadWriteCloser) *Conn { } // Close closes the connection. -func (c *Conn) Close() os.Error { +func (c *Conn) Close() error { return c.conn.Close() } // Dial connects to the given address on the given network using net.Dial // and then returns a new Conn for the connection. -func Dial(network, addr string) (*Conn, os.Error) { +func Dial(network, addr string) (*Conn, error) { c, err := net.Dial(network, addr) if err != nil { return nil, err @@ -109,7 +108,7 @@ func Dial(network, addr string) (*Conn, os.Error) { // } // return c.ReadCodeLine(250) // -func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err os.Error) { +func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err error) { id = c.Next() c.StartRequest(id) err = c.PrintfLine(format, args...) diff --git a/src/pkg/net/textproto/writer.go b/src/pkg/net/textproto/writer.go index 4e705f6c3..03e2fd658 100644 --- a/src/pkg/net/textproto/writer.go +++ b/src/pkg/net/textproto/writer.go @@ -8,7 +8,6 @@ import ( "bufio" "fmt" "io" - "os" ) // A Writer implements convenience methods for writing @@ -27,7 +26,7 @@ var crnl = []byte{'\r', '\n'} var dotcrnl = []byte{'.', '\r', '\n'} // PrintfLine writes the formatted output followed by \r\n. -func (w *Writer) PrintfLine(format string, args ...interface{}) os.Error { +func (w *Writer) PrintfLine(format string, args ...interface{}) error { w.closeDot() fmt.Fprintf(w.W, format, args...) w.W.Write(crnl) @@ -64,7 +63,7 @@ const ( wstateData // writing data in middle of line ) -func (d *dotWriter) Write(b []byte) (n int, err os.Error) { +func (d *dotWriter) Write(b []byte) (n int, err error) { bw := d.w.W for n < len(b) { c := b[n] @@ -100,7 +99,7 @@ func (d *dotWriter) Write(b []byte) (n int, err os.Error) { return } -func (d *dotWriter) Close() os.Error { +func (d *dotWriter) Close() error { if d.w.dot == d { d.w.dot = nil } |