diff options
Diffstat (limited to 'vendor/github.com/cretz/bine/control/conn.go')
-rw-r--r-- | vendor/github.com/cretz/bine/control/conn.go | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/vendor/github.com/cretz/bine/control/conn.go b/vendor/github.com/cretz/bine/control/conn.go new file mode 100644 index 0000000..02995e8 --- /dev/null +++ b/vendor/github.com/cretz/bine/control/conn.go @@ -0,0 +1,102 @@ +package control + +import ( + "fmt" + "io" + "net/textproto" + "sync" +) + +// Conn is the connection to the Tor control port. +type Conn struct { + // DebugWriter is the writer that debug logs for this library (not Tor + // itself) will be written to. If nil, no debug logs are generated/written. + DebugWriter io.Writer + + // This is the underlying connection. + conn *textproto.Conn + + // This is set lazily by ProtocolInfo(). + protocolInfo *ProtocolInfo + + // True if Authenticate has been called successfully. + Authenticated bool + + // The lock fot eventListeners + eventListenersLock sync.RWMutex + // The value slices can be traversed outside of lock, they are completely + // replaced on change, never mutated. But the map itself must be locked on + // when reading or writing. + eventListeners map[EventCode][]chan<- Event + + // This mutex is locked on when an entire response needs to be read. It + // helps synchronize accesses to the response by the asynchronous response + // listeners and the synchronous responses. + readLock sync.Mutex +} + +// NewConn creates a Conn from the given textproto connection. +func NewConn(conn *textproto.Conn) *Conn { + return &Conn{ + conn: conn, + eventListeners: map[EventCode][]chan<- Event{}, + } +} + +func (c *Conn) sendRequestIgnoreResponse(format string, args ...interface{}) error { + _, err := c.SendRequest(format, args...) + return err +} + +// SendRequest sends a synchronous request to Tor and awaits the response. If +// the response errors, the error result will be set, but the response will be +// set also. This is usually not directly used by callers, but instead called by +// higher-level methods. +func (c *Conn) SendRequest(format string, args ...interface{}) (*Response, error) { + if c.debugEnabled() { + c.debugf("Write line: %v", fmt.Sprintf(format, args...)) + } + id, err := c.conn.Cmd(format, args...) + if err != nil { + return nil, err + } + c.readLock.Lock() + defer c.readLock.Unlock() + c.conn.StartResponse(id) + defer c.conn.EndResponse(id) + // Get the first non-async response + var resp *Response + for { + if resp, err = c.ReadResponse(); err != nil || !resp.IsAsync() { + break + } + c.relayAsyncEvents(resp) + } + if err == nil && !resp.IsOk() { + err = resp.Err + } + return resp, err +} + +// Close sends a QUIT and closes the underlying Tor connection. This does not +// error if the QUIT is not accepted but does relay any error that occurs while +// closing the underlying connection. +func (c *Conn) Close() error { + // Ignore the response and ignore the error + c.Quit() + return c.conn.Close() +} + +func (c *Conn) debugEnabled() bool { + return c.DebugWriter != nil +} + +func (c *Conn) debugf(format string, args ...interface{}) { + if w := c.DebugWriter; w != nil { + fmt.Fprintf(w, format+"\n", args...) + } +} + +func (*Conn) protoErr(format string, args ...interface{}) textproto.ProtocolError { + return textproto.ProtocolError(fmt.Sprintf(format, args...)) +} |