11package dtx
22
33import (
4+ "errors"
45 "io"
56 "math"
67 "strings"
@@ -15,6 +16,8 @@ import (
1516
1617type MethodWithResponse func (msg Message ) (interface {}, error )
1718
19+ var ErrConnectionClosed = errors .New ("Connection closed" )
20+
1821// Connection manages channels, including the GlobalChannel, for a DtxConnection and dispatches received messages
1922// to the right channel.
2023type Connection struct {
@@ -25,6 +28,10 @@ type Connection struct {
2528 capabilities map [string ]interface {}
2629 mutex sync.Mutex
2730 requestChannelMessages chan Message
31+
32+ closed chan struct {}
33+ err error
34+ closeOnce sync.Once
2835}
2936
3037// Dispatcher is a simple interface containing a Dispatch func to receive dtx.Messages
@@ -41,11 +48,24 @@ type GlobalDispatcher struct {
4148
4249const requestChannel = "_requestChannelWithCode:identifier:"
4350
51+ // Closed is closed when the underlying DTX connection was closed for any reason (either initiated by calling Close() or due to an error)
52+ func (dtxConn * Connection ) Closed () <- chan struct {} {
53+ return dtxConn .closed
54+ }
55+
56+ // Err is non-nil when the connection was closed (when Close was called this will be ErrConnectionClosed)
57+ func (dtxConn * Connection ) Err () error {
58+ return dtxConn .err
59+ }
60+
4461// Close closes the underlying deviceConnection
4562func (dtxConn * Connection ) Close () error {
4663 if dtxConn .deviceConnection != nil {
47- return dtxConn .deviceConnection .Close ()
64+ err := dtxConn .deviceConnection .Close ()
65+ dtxConn .close (err )
66+ return err
4867 }
68+ dtxConn .close (ErrConnectionClosed )
4969 return nil
5070}
5171
@@ -121,6 +141,7 @@ func newDtxConnection(conn ios.DeviceConnectionInterface) (*Connection, error) {
121141
122142 // The global channel has channelCode 0, so we need to start with channelCodeCounter==1
123143 dtxConnection := & Connection {deviceConnection : conn , channelCodeCounter : 1 , requestChannelMessages : requestChannelMessages }
144+ dtxConnection .closed = make (chan struct {})
124145
125146 // The global channel is automatically present and used for requesting other channels and some other methods like notifyPublishedCapabilities
126147 globalChannel := Channel {
@@ -149,6 +170,7 @@ func reader(dtxConn *Connection) {
149170 reader := dtxConn .deviceConnection .Reader ()
150171 msg , err := ReadMessage (reader )
151172 if err != nil {
173+ defer dtxConn .close (err )
152174 errText := err .Error ()
153175 if err == io .EOF || strings .Contains (errText , "use of closed network" ) {
154176 log .Debug ("DTX Connection with EOF" )
@@ -228,3 +250,10 @@ func (dtxConn *Connection) RequestChannelIdentifier(identifier string, messageDi
228250
229251 return channel
230252}
253+
254+ func (dtxConn * Connection ) close (err error ) {
255+ dtxConn .closeOnce .Do (func () {
256+ dtxConn .err = err
257+ close (dtxConn .closed )
258+ })
259+ }
0 commit comments