github.com/philippseith/signalr@v0.6.3/connectionbase.go (about) 1 package signalr 2 3 import ( 4 "context" 5 "sync" 6 ) 7 8 // ConnectionBase is a baseclass for implementers of the Connection interface. 9 type ConnectionBase struct { 10 mx sync.RWMutex 11 ctx context.Context 12 connectionID string 13 } 14 15 // NewConnectionBase creates a new ConnectionBase 16 func NewConnectionBase(ctx context.Context, connectionID string) *ConnectionBase { 17 cb := &ConnectionBase{ 18 ctx: ctx, 19 connectionID: connectionID, 20 } 21 return cb 22 } 23 24 // Context can be used to wait for cancellation of the Connection 25 func (cb *ConnectionBase) Context() context.Context { 26 cb.mx.RLock() 27 defer cb.mx.RUnlock() 28 return cb.ctx 29 } 30 31 // ConnectionID is the ID of the connection. 32 func (cb *ConnectionBase) ConnectionID() string { 33 cb.mx.RLock() 34 defer cb.mx.RUnlock() 35 return cb.connectionID 36 } 37 38 // SetConnectionID sets the ConnectionID 39 func (cb *ConnectionBase) SetConnectionID(id string) { 40 cb.mx.Lock() 41 defer cb.mx.Unlock() 42 cb.connectionID = id 43 } 44 45 // ReadWriteWithContext is a wrapper to make blocking io.Writer / io.Reader cancelable. 46 // It can be used to implement cancellation of connections. 47 // ReadWriteWithContext will return when either the Read/Write operation has ended or ctx has been canceled. 48 // doRW func() (int, error) 49 // doRW should contain the Read/Write operation. 50 // unblockRW func() 51 // unblockRW should contain the operation to unblock the Read/Write operation. 52 // If there is no way to unblock the operation, one goroutine will leak when ctx is canceled. 53 // As the standard use case when ReadWriteWithContext is canceled is the cancellation of a connection this leak 54 // will be problematic on heavily used servers with uncommon connection types. Luckily, the standard connection types 55 // for ServerSentEvents, Websockets and common net.Conn connections can be unblocked. 56 func ReadWriteWithContext(ctx context.Context, doRW func() (int, error), unblockRW func()) (int, error) { 57 if ctx.Err() != nil { 58 return 0, ctx.Err() 59 } 60 resultChan := make(chan RWJobResult, 1) 61 go func() { 62 n, err := doRW() 63 resultChan <- RWJobResult{n: n, err: err} 64 close(resultChan) 65 }() 66 select { 67 case <-ctx.Done(): 68 unblockRW() 69 return 0, ctx.Err() 70 case r := <-resultChan: 71 return r.n, r.err 72 } 73 } 74 75 // RWJobResult can be used to send the result of an io.Writer / io.Reader operation over a channel. 76 // Use it for special connection types, where ReadWriteWithContext does not fit all needs. 77 type RWJobResult struct { 78 n int 79 err error 80 }