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  }