summaryrefslogtreecommitdiff
path: root/vendor/github.com/pion/stun/agent.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/pion/stun/agent.go')
-rw-r--r--vendor/github.com/pion/stun/agent.go228
1 files changed, 228 insertions, 0 deletions
diff --git a/vendor/github.com/pion/stun/agent.go b/vendor/github.com/pion/stun/agent.go
new file mode 100644
index 0000000..6a8a473
--- /dev/null
+++ b/vendor/github.com/pion/stun/agent.go
@@ -0,0 +1,228 @@
+package stun
+
+import (
+ "errors"
+ "sync"
+ "time"
+)
+
+// NoopHandler just discards any event.
+var NoopHandler Handler = func(e Event) {}
+
+// NewAgent initializes and returns new Agent with provided handler.
+// If h is nil, the NoopHandler will be used.
+func NewAgent(h Handler) *Agent {
+ if h == nil {
+ h = NoopHandler
+ }
+ a := &Agent{
+ transactions: make(map[transactionID]agentTransaction),
+ handler: h,
+ }
+ return a
+}
+
+// Agent is low-level abstraction over transaction list that
+// handles concurrency (all calls are goroutine-safe) and
+// time outs (via Collect call).
+type Agent struct {
+ // transactions is map of transactions that are currently
+ // in progress. Event handling is done in such way when
+ // transaction is unregistered before agentTransaction access,
+ // minimizing mux lock and protecting agentTransaction from
+ // data races via unexpected concurrent access.
+ transactions map[transactionID]agentTransaction
+ closed bool // all calls are invalid if true
+ mux sync.Mutex // protects transactions and closed
+ handler Handler // handles transactions
+}
+
+// Handler handles state changes of transaction.
+//
+// Handler is called on transaction state change.
+// Usage of e is valid only during call, user must
+// copy needed fields explicitly.
+type Handler func(e Event)
+
+// Event is passed to Handler describing the transaction event.
+// Do not reuse outside Handler.
+type Event struct {
+ TransactionID [TransactionIDSize]byte
+ Message *Message
+ Error error
+}
+
+// agentTransaction represents transaction in progress.
+// Concurrent access is invalid.
+type agentTransaction struct {
+ id transactionID
+ deadline time.Time
+}
+
+var (
+ // ErrTransactionStopped indicates that transaction was manually stopped.
+ ErrTransactionStopped = errors.New("transaction is stopped")
+ // ErrTransactionNotExists indicates that agent failed to find transaction.
+ ErrTransactionNotExists = errors.New("transaction not exists")
+ // ErrTransactionExists indicates that transaction with same id is already
+ // registered.
+ ErrTransactionExists = errors.New("transaction exists with same id")
+)
+
+// StopWithError removes transaction from list and calls handler with
+// provided error. Can return ErrTransactionNotExists and ErrAgentClosed.
+func (a *Agent) StopWithError(id [TransactionIDSize]byte, err error) error {
+ a.mux.Lock()
+ if a.closed {
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ t, exists := a.transactions[id]
+ delete(a.transactions, id)
+ h := a.handler
+ a.mux.Unlock()
+ if !exists {
+ return ErrTransactionNotExists
+ }
+ h(Event{
+ TransactionID: t.id,
+ Error: err,
+ })
+ return nil
+}
+
+// Stop stops transaction by id with ErrTransactionStopped, blocking
+// until handler returns.
+func (a *Agent) Stop(id [TransactionIDSize]byte) error {
+ return a.StopWithError(id, ErrTransactionStopped)
+}
+
+// ErrAgentClosed indicates that agent is in closed state and is unable
+// to handle transactions.
+var ErrAgentClosed = errors.New("agent is closed")
+
+// Start registers transaction with provided id and deadline.
+// Could return ErrAgentClosed, ErrTransactionExists.
+//
+// Agent handler is guaranteed to be eventually called.
+func (a *Agent) Start(id [TransactionIDSize]byte, deadline time.Time) error {
+ a.mux.Lock()
+ defer a.mux.Unlock()
+ if a.closed {
+ return ErrAgentClosed
+ }
+ _, exists := a.transactions[id]
+ if exists {
+ return ErrTransactionExists
+ }
+ a.transactions[id] = agentTransaction{
+ id: id,
+ deadline: deadline,
+ }
+ return nil
+}
+
+// agentCollectCap is initial capacity for Agent.Collect slices,
+// sufficient to make function zero-alloc in most cases.
+const agentCollectCap = 100
+
+// ErrTransactionTimeOut indicates that transaction has reached deadline.
+var ErrTransactionTimeOut = errors.New("transaction is timed out")
+
+// Collect terminates all transactions that have deadline before provided
+// time, blocking until all handlers will process ErrTransactionTimeOut.
+// Will return ErrAgentClosed if agent is already closed.
+//
+// It is safe to call Collect concurrently but makes no sense.
+func (a *Agent) Collect(gcTime time.Time) error {
+ toRemove := make([]transactionID, 0, agentCollectCap)
+ a.mux.Lock()
+ if a.closed {
+ // Doing nothing if agent is closed.
+ // All transactions should be already closed
+ // during Close() call.
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ // Adding all transactions with deadline before gcTime
+ // to toCall and toRemove slices.
+ // No allocs if there are less than agentCollectCap
+ // timed out transactions.
+ for id, t := range a.transactions {
+ if t.deadline.Before(gcTime) {
+ toRemove = append(toRemove, id)
+ }
+ }
+ // Un-registering timed out transactions.
+ for _, id := range toRemove {
+ delete(a.transactions, id)
+ }
+ // Calling handler does not require locked mutex,
+ // reducing lock time.
+ h := a.handler
+ a.mux.Unlock()
+ // Sending ErrTransactionTimeOut to handler for all transactions,
+ // blocking until last one.
+ event := Event{
+ Error: ErrTransactionTimeOut,
+ }
+ for _, id := range toRemove {
+ event.TransactionID = id
+ h(event)
+ }
+ return nil
+}
+
+// Process incoming message, synchronously passing it to handler.
+func (a *Agent) Process(m *Message) error {
+ e := Event{
+ TransactionID: m.TransactionID,
+ Message: m,
+ }
+ a.mux.Lock()
+ if a.closed {
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ h := a.handler
+ delete(a.transactions, m.TransactionID)
+ a.mux.Unlock()
+ h(e)
+ return nil
+}
+
+// SetHandler sets agent handler to h.
+func (a *Agent) SetHandler(h Handler) error {
+ a.mux.Lock()
+ if a.closed {
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ a.handler = h
+ a.mux.Unlock()
+ return nil
+}
+
+// Close terminates all transactions with ErrAgentClosed and renders Agent to
+// closed state.
+func (a *Agent) Close() error {
+ e := Event{
+ Error: ErrAgentClosed,
+ }
+ a.mux.Lock()
+ if a.closed {
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ for _, t := range a.transactions {
+ e.TransactionID = t.id
+ a.handler(e)
+ }
+ a.transactions = nil
+ a.closed = true
+ a.handler = nil
+ a.mux.Unlock()
+ return nil
+}
+
+type transactionID [TransactionIDSize]byte