package client
import (
"errors"
"fmt"
"net"
"sync"
"time"
"github.com/pion/logging"
"github.com/pion/stun/v3"
"github.com/pion/transport/v3"
"github.com/pion/turn/v4/internal/proto"
)
type AllocationConfig struct {
Client Client
RelayedAddr net .Addr
ServerAddr net .Addr
Integrity stun .MessageIntegrity
Nonce stun .Nonce
Username stun .Username
Realm stun .Realm
Lifetime time .Duration
Net transport .Net
Log logging .LeveledLogger
}
type allocation struct {
client Client
relayedAddr net .Addr
serverAddr net .Addr
permMap *permissionMap
integrity stun .MessageIntegrity
username stun .Username
realm stun .Realm
_nonce stun .Nonce
_lifetime time .Duration
net transport .Net
refreshAllocTimer *PeriodicTimer
refreshPermsTimer *PeriodicTimer
readTimer *time .Timer
mutex sync .RWMutex
log logging .LeveledLogger
}
func (a *allocation ) setNonceFromMsg (msg *stun .Message ) {
var nonce stun .Nonce
if err := nonce .GetFrom (msg ); err == nil {
a .setNonce (nonce )
a .log .Debug ("Refresh allocation: 438, got new nonce." )
} else {
a .log .Warn ("Refresh allocation: 438 but no nonce." )
}
}
func (a *allocation ) refreshAllocation (lifetime time .Duration , dontWait bool ) error {
msg , err := stun .Build (
stun .TransactionID ,
stun .NewType (stun .MethodRefresh , stun .ClassRequest ),
proto .Lifetime {Duration : lifetime },
a .username ,
a .realm ,
a .nonce (),
a .integrity ,
stun .Fingerprint ,
)
if err != nil {
return fmt .Errorf ("%w: %s" , errFailedToBuildRefreshRequest , err .Error())
}
a .log .Debugf ("Send refresh request (dontWait=%v)" , dontWait )
trRes , err := a .client .PerformTransaction (msg , a .serverAddr , dontWait )
if err != nil {
return fmt .Errorf ("%w: %s" , errFailedToRefreshAllocation , err .Error())
}
if dontWait {
a .log .Debug ("Refresh request sent" )
return nil
}
a .log .Debug ("Refresh request sent, and waiting response" )
res := trRes .Msg
if res .Type .Class == stun .ClassErrorResponse {
var code stun .ErrorCodeAttribute
if err = code .GetFrom (res ); err == nil {
if code .Code == stun .CodeStaleNonce {
a .setNonceFromMsg (res )
return errTryAgain
}
return err
}
return fmt .Errorf ("%s" , res .Type )
}
var updatedLifetime proto .Lifetime
if err := updatedLifetime .GetFrom (res ); err != nil {
return fmt .Errorf ("%w: %s" , errFailedToGetLifetime , err .Error())
}
a .setLifetime (updatedLifetime .Duration )
a .log .Debugf ("Updated lifetime: %d seconds" , int (a .lifetime ().Seconds ()))
return nil
}
func (a *allocation ) refreshPermissions () error {
addrs := a .permMap .addrs ()
if len (addrs ) == 0 {
a .log .Debug ("No permission to refresh" )
return nil
}
if err := a .CreatePermissions (addrs ...); err != nil {
if errors .Is (err , errTryAgain ) {
return errTryAgain
}
a .log .Errorf ("Fail to refresh permissions: %s" , err )
return err
}
a .log .Debug ("Refresh permissions successful" )
return nil
}
func (a *allocation ) onRefreshTimers (id int ) {
a .log .Debugf ("Refresh timer %d expired" , id )
switch id {
case timerIDRefreshAlloc :
var err error
lifetime := a .lifetime ()
for i := 0 ; i < maxRetryAttempts ; i ++ {
err = a .refreshAllocation (lifetime , false )
if !errors .Is (err , errTryAgain ) {
break
}
}
if err != nil {
a .log .Warnf ("Failed to refresh allocation: %s" , err )
}
case timerIDRefreshPerms :
var err error
for i := 0 ; i < maxRetryAttempts ; i ++ {
err = a .refreshPermissions ()
if !errors .Is (err , errTryAgain ) {
break
}
}
if err != nil {
a .log .Warnf ("Failed to refresh permissions: %s" , err )
}
}
}
func (a *allocation ) nonce () stun .Nonce {
a .mutex .RLock ()
defer a .mutex .RUnlock ()
return a ._nonce
}
func (a *allocation ) setNonce (nonce stun .Nonce ) {
a .mutex .Lock ()
defer a .mutex .Unlock ()
a .log .Debugf ("Set new nonce with %d bytes" , len (nonce ))
a ._nonce = nonce
}
func (a *allocation ) lifetime () time .Duration {
a .mutex .RLock ()
defer a .mutex .RUnlock ()
return a ._lifetime
}
func (a *allocation ) setLifetime (lifetime time .Duration ) {
a .mutex .Lock ()
defer a .mutex .Unlock ()
a ._lifetime = lifetime
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .