summaryrefslogtreecommitdiff
path: root/vendor/github.com/cretz/bine/control/conn.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cretz/bine/control/conn.go')
-rw-r--r--vendor/github.com/cretz/bine/control/conn.go102
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...))
+}