diff --git a/config/config_defaults.go b/config/config_defaults.go index 9d1cc2d87..8f1d96149 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -210,7 +210,9 @@ const CGRATES_CFG_JSON = ` "debit_interval": "0s", // interval to perform debits on. "min_call_duration": "0s", // only authorize calls with allowed duration higher than this "max_call_duration": "3h", // maximum call duration a prepaid call can last - "session_ttl": "0s", // time after a session with no updates is terminated + "session_ttl": "0s", // time after a session with no updates is terminated, not defined by default + //"session_ttl_last_used": "", // tweak LastUsed for sessions timing-out, not defined by default + //"session_ttl_usage": "", // tweak Usage for sessions timing-out, not defined by default }, diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 2dca787a4..82d9469f3 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -167,14 +167,16 @@ type CdrcJsonCfg struct { // SM-Generic config section type SmGenericJsonCfg struct { - Enabled *bool - Listen_bijson *string - Rater *string - Cdrs *string - Debit_interval *string - Min_call_duration *string - Max_call_duration *string - Session_ttl *string + Enabled *bool + Listen_bijson *string + Rater *string + Cdrs *string + Debit_interval *string + Min_call_duration *string + Max_call_duration *string + Session_ttl *string + Session_ttl_last_used *string + Session_ttl_usage *string } // SM-FreeSWITCH config section diff --git a/config/smconfig.go b/config/smconfig.go index e828c76a0..fe883576c 100644 --- a/config/smconfig.go +++ b/config/smconfig.go @@ -72,14 +72,16 @@ func (self *FsConnConfig) loadFromJsonCfg(jsnCfg *FsConnJsonCfg) error { } type SmGenericConfig struct { - Enabled bool - ListenBijson string - HaRater []*HaPoolConfig - HaCdrs []*HaPoolConfig - DebitInterval time.Duration - MinCallDuration time.Duration - MaxCallDuration time.Duration - SessionTTL time.Duration + Enabled bool + ListenBijson string + HaRater []*HaPoolConfig + HaCdrs []*HaPoolConfig + DebitInterval time.Duration + MinCallDuration time.Duration + MaxCallDuration time.Duration + SessionTTL time.Duration + SessionTTLLastUsed *time.Duration + SessionTTLUsage *time.Duration } func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error { @@ -119,6 +121,20 @@ func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error { return err } } + if jsnCfg.Session_ttl_last_used != nil { + if sessionTTLLastUsed, err := utils.ParseDurationWithSecs(*jsnCfg.Session_ttl_last_used); err != nil { + return err + } else { + self.SessionTTLLastUsed = &sessionTTLLastUsed + } + } + if jsnCfg.Session_ttl_usage != nil { + if sessionTTLUsage, err := utils.ParseDurationWithSecs(*jsnCfg.Session_ttl_usage); err != nil { + return err + } else { + self.SessionTTLUsage = &sessionTTLUsage + } + } return nil } diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index fd086efbf..68afa1ea5 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -51,34 +51,75 @@ type SMGeneric struct { guard *engine.GuardianLock // Used to lock on uuid } type smgSessionTerminator struct { - timer *time.Timer - endChan chan bool + timer *time.Timer + endChan chan bool + ttl time.Duration + ttlLastUsed *time.Duration + ttlUsage *time.Duration +} + +// Updates the timer for the session to a new ttl and terminate info +func (self *SMGeneric) resetTerminatorTimer(uuid string, ttl time.Duration, ttlLastUsed, ttlUsage *time.Duration) { + self.sessionsMux.RLock() + defer self.sessionsMux.RUnlock() + if st, found := self.sessionTerminators[uuid]; found { + if ttl != 0 { + st.ttl = ttl + } + if ttlLastUsed != nil { + st.ttlLastUsed = ttlLastUsed + } + if ttlUsage != nil { + st.ttlUsage = ttlUsage + } + st.timer.Reset(st.ttl) + } +} + +// Called when a session timeouts +func (self *SMGeneric) ttlTerminate(s *SMGSession, tmtr *smgSessionTerminator) { + totalSessionUsage := s.TotalUsage() + tmtr.ttl + if tmtr.ttlUsage != nil { + totalSessionUsage = *tmtr.ttlUsage + } + if totalSessionUsage > s.TotalUsage() { + evUpdate := s.eventStart + diffSessionUsage := totalSessionUsage - s.TotalUsage() + evUpdate[utils.USAGE] = diffSessionUsage.Seconds() // Debit additionally + if tmtr.ttlLastUsed != nil { + evUpdate[utils.LastUsed] = tmtr.ttlLastUsed.Seconds() + } + self.SessionUpdate(evUpdate, nil) + } + self.sessionEnd(s.eventStart.GetUUID(), totalSessionUsage) + cdr := s.eventStart.AsStoredCdr(self.cgrCfg, self.timezone) + cdr.Usage = totalSessionUsage + var reply string + self.cdrsrv.ProcessCdr(cdr, &reply) } func (self *SMGeneric) indexSession(uuid string, s *SMGSession) { self.sessionsMux.Lock() self.sessions[uuid] = append(self.sessions[uuid], s) - if self.cgrCfg.SmGenericConfig.SessionTTL > 0 { + if self.cgrCfg.SmGenericConfig.SessionTTL != 0 { if _, found := self.sessionTerminators[uuid]; !found { timer := time.NewTimer(self.cgrCfg.SmGenericConfig.SessionTTL) endChan := make(chan bool, 1) + terminator := &smgSessionTerminator{ + timer: timer, + endChan: endChan, + ttl: self.cgrCfg.SmGenericConfig.SessionTTL, + } + self.sessionTerminators[uuid] = terminator go func() { select { case <-timer.C: - totalUsage := s.TotalUsage() + self.cgrCfg.SmGenericConfig.SessionTTL - self.sessionEnd(uuid, totalUsage) - cdr := s.eventStart.AsStoredCdr(self.cgrCfg, self.timezone) - cdr.Usage = totalUsage - var reply string - self.cdrsrv.ProcessCdr(cdr, &reply) + self.ttlTerminate(s, terminator) case <-endChan: timer.Stop() } }() - self.sessionTerminators[uuid] = &smgSessionTerminator{ - timer: timer, - endChan: endChan, - } + } } self.sessionsMux.Unlock() @@ -125,15 +166,6 @@ func (self *SMGeneric) getSession(uuid string) []*SMGSession { return self.sessions[uuid] } -// Updates the timer for the session to a new ttl -func (self *SMGeneric) resetTerminatorTimer(uuid string) { - self.sessionsMux.RLock() - defer self.sessionsMux.RUnlock() - if st, found := self.sessionTerminators[uuid]; found { - st.timer.Reset(self.cgrCfg.SmGenericConfig.SessionTTL) - } -} - // Handle a new session, pass the connectionId so we can communicate on disconnect request func (self *SMGeneric) sessionStart(evStart SMGenericEvent, connId string) error { sessionId := evStart.GetUUID() @@ -262,7 +294,21 @@ func (self *SMGeneric) SessionStart(gev SMGenericEvent, clnt *rpc2.Client) (time // Execute debits for usage/maxUsage func (self *SMGeneric) SessionUpdate(gev SMGenericEvent, clnt *rpc2.Client) (time.Duration, error) { - self.resetTerminatorTimer(gev.GetUUID()) + ttl := time.Duration(0) + if ttlStr, err := gev.GetFieldAsString(utils.SessionTTL); err == nil { + ttl, _ = utils.ParseDurationWithSecs(ttlStr) + } + var ttlLastUsed *time.Duration + if ttlLastUsedStr, err := gev.GetFieldAsString(utils.SessionTTLLastUsed); err == nil { + ttlLastUsedParsed, _ := utils.ParseDurationWithSecs(ttlLastUsedStr) + ttlLastUsed = &ttlLastUsedParsed + } + var ttlUsage *time.Duration + if ttlUsageStr, err := gev.GetFieldAsString(utils.SessionTTLUsage); err == nil { + ttlUsageParsed, _ := utils.ParseDurationWithSecs(ttlUsageStr) + ttlUsage = &ttlUsageParsed + } + self.resetTerminatorTimer(gev.GetUUID(), ttl, ttlLastUsed, ttlUsage) if initialID, err := gev.GetFieldAsString(utils.InitialOriginID); err == nil { err := self.sessionRelocate(gev.GetUUID(), initialID) if err == utils.ErrNotFound { // Session was already relocated, create a new session with this update diff --git a/utils/consts.go b/utils/consts.go index 0bacf323e..b83e10808 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -278,6 +278,9 @@ const ( UpdatedAt = "UpdatedAt" HandlerArgSep = "|" FlagForceDuration = "fd" + SessionTTL = "SessionTTL" + SessionTTLLastUsed = "SessionTTLLastUsed" + SessionTTLUsage = "SessionTTLUsage" ) var (