Merge pull request #4 from leif/RawLines

make ReadResponse public, and add RawLines
This commit is contained in:
Yawning Angel 2016-08-21 22:24:25 +00:00 committed by GitHub
commit 2d65b370c8
2 changed files with 27 additions and 7 deletions

14
conn.go
View File

@ -66,7 +66,7 @@ func (c *Conn) isAsyncReaderRunning() bool {
func (c *Conn) asyncReader() {
for {
resp, err := c.readResponse()
resp, err := c.ReadResponse()
if err != nil {
c.setRdErr(err, false)
break
@ -107,7 +107,7 @@ func (c *Conn) Close() error {
// StartAsyncReader starts the asynchronous reader go routine that allows
// asynchronous events to be handled. It must not be called simultaniously
// with Request or undefined behavior will occur.
// with Read, Request, or ReadResponse or undefined behavior will occur.
func (c *Conn) StartAsyncReader() {
c.asyncReaderLock.Lock()
defer c.asyncReaderLock.Unlock()
@ -145,8 +145,9 @@ func (c *Conn) NextEvent() (*Response, error) {
// Request sends a raw control port request and returns the response.
// If the async. reader is not currently running, events received while waiting
// for the response will be silently dropped. Calling StartAsyncReader
// simultaniously with Request will lead to undefined behavior.
// for the response will be silently dropped. Calling Request simultaniously
// with StartAsyncReader, Read, Write, or ReadResponse will lead to undefined
// behavior.
func (c *Conn) Request(fmt string, args ...interface{}) (*Response, error) {
if err := c.getRdErr(); err != nil {
return nil, err
@ -175,7 +176,7 @@ func (c *Conn) Request(fmt string, args ...interface{}) (*Response, error) {
// Event handing requires the asyncReader() goroutine, try to get a
// response, while silently swallowing events.
for resp == nil || resp.IsAsync() {
resp, err = c.readResponse()
resp, err = c.ReadResponse()
if err != nil {
return nil, err
}
@ -191,7 +192,8 @@ func (c *Conn) Request(fmt string, args ...interface{}) (*Response, error) {
}
// Read reads directly from the control port connection. Mixing this call
// with Request, or asynchronous events will lead to undefined behavior.
// with Request, ReadResponse, or asynchronous events will lead to undefined
// behavior.
func (c *Conn) Read(p []byte) (int, error) {
return c.conn.R.Read(p)
}

View File

@ -11,6 +11,7 @@ import (
"log"
"net/textproto"
"strconv"
"strings"
)
// Response is a response to a control port command, or an asyncrhonous event.
@ -27,6 +28,9 @@ type Response struct {
// data is "decoded" and presented as a single string (terminal ".CRLF"
// removed, all intervening CRs stripped).
Data []string
// RawLines is all of the lines of a response, without CRLFs.
RawLines []string
}
// IsOk returns true if the response status code indicates success or
@ -45,7 +49,10 @@ func (r *Response) IsAsync() bool {
return r.Err.Code == StatusAsyncEvent
}
func (c *Conn) readResponse() (*Response, error) {
// ReadResponse returns the next response object. Calling this
// simultaniously with Read, Request, or StartAsyncReader will lead to
// undefined behavior
func (c *Conn) ReadResponse() (*Response, error) {
var resp *Response
var statusCode int
for {
@ -74,11 +81,15 @@ func (c *Conn) readResponse() (*Response, error) {
// lines.
return nil, newProtocolError("status code changed: %03d != %03d", code, statusCode)
}
if resp.RawLines == nil {
resp.RawLines = make([]string, 0, 1)
}
if line[3] == ' ' {
// Final line in the response.
resp.Reply = line[4:]
resp.Err = statusCodeToError(statusCode, resp.Reply)
resp.RawLines = append(resp.RawLines, line)
return resp, nil
}
@ -89,9 +100,11 @@ func (c *Conn) readResponse() (*Response, error) {
case '-':
// Continuation, keep reading.
resp.Data = append(resp.Data, line[4:])
resp.RawLines = append(resp.RawLines, line)
case '+':
// A "dot-encoded" payload follows.
resp.Data = append(resp.Data, line[4:])
resp.RawLines = append(resp.RawLines, line)
dotBody, err := c.conn.ReadDotBytes()
if err != nil {
return nil, err
@ -100,6 +113,11 @@ func (c *Conn) readResponse() (*Response, error) {
log.Printf("S: [dot encoded data]")
}
resp.Data = append(resp.Data, string(dotBody))
dotLines := strings.Split(string(dotBody), "\n")
for _, dotLine := range dotLines[:len(dotLines)-1] {
resp.RawLines = append(resp.RawLines, dotLine)
}
resp.RawLines = append(resp.RawLines, ".")
default:
return nil, newProtocolError("invalid separator: '%c'", line[3])
}