Skip to content

Commit

Permalink
simplify tunnel map implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
jakecoffman committed Nov 19, 2019
1 parent edd438c commit bacca8e
Showing 1 changed file with 31 additions and 84 deletions.
115 changes: 31 additions & 84 deletions tunnel_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ multiple requests, tracking of activity on the tunnel must be performed
independently of the HTTP requests.
*/
type LastAccessedTunnel struct {
sync.RWMutex
Tunnel
lastAccessedTime time.Time
}
Expand All @@ -28,10 +29,14 @@ func NewLastAccessedTunnel(tunnel Tunnel) (ret LastAccessedTunnel) {
}

func (t *LastAccessedTunnel) Access() {
t.Lock()
t.lastAccessedTime = time.Now()
t.Unlock()
}

func (t *LastAccessedTunnel) GetLastAccessedTime() time.Time {
t.RLock()
defer t.RUnlock()
return t.lastAccessedTime
}

Expand All @@ -50,41 +55,30 @@ intended for use only within the Server implementation,
and has no real utility outside that implementation.
*/
type TunnelMap struct {
// Executor service which runs the periodic tunnel timeout task.
executor []*time.Ticker
sync.RWMutex
ticker *time.Ticker

// tunnelTimeout is the maximum amount of time to allow between accesses to any one HTTP tunnel.
tunnelTimeout time.Duration

// Map of all tunnels that are using HTTP, indexed by tunnel UUID.
tunnelMap map[string]*LastAccessedTunnel
tunnelMapLock sync.RWMutex
}

// NewTunnelMap creates a new TunnelMap which automatically closes and removes HTTP tunnels which are no longer in use.
// NewTunnelMap creates a new TunnelMap and starts the scheduled job with the default timeout.
func NewTunnelMap() *TunnelMap {
tunnelMap := &TunnelMap{
executor: make([]*time.Ticker, 0, 1),
tunnelMap: make(map[string]*LastAccessedTunnel),
ticker: time.NewTicker(TunnelTimeout),
tunnelMap: make(map[string]*LastAccessedTunnel),
tunnelTimeout: TunnelTimeout,
}
tunnelMap.startScheduled(1, TunnelTimeout)
go tunnelMap.tunnelTimeoutTask()
return tunnelMap
}

func (m *TunnelMap) startScheduled(count int32, timeout time.Duration) {
for i := int32(len(m.executor)); i < count; i++ {

tick := time.NewTicker(timeout)
go m.tunnelTimeoutTask(tick.C)

m.executor = append(m.executor, tick)
}
}

func (m *TunnelMap) tunnelTimeoutTask(c <-chan time.Time) {
func (m *TunnelMap) tunnelTimeoutTask() {
for {
_, ok := <-c
_, ok := <-m.ticker.C
if !ok {
break
}
Expand All @@ -93,28 +87,26 @@ func (m *TunnelMap) tunnelTimeoutTask(c <-chan time.Time) {
}

func (m *TunnelMap) tunnelTimeoutTaskRun() {
// timeLine = Now() - tunnelTimeout
timeLine := time.Now().Add(0 - m.tunnelTimeout)

type pair struct {
uuid string
tunnel *LastAccessedTunnel
}
removeIDs := make([]pair, 0, 1)
var removeIDs []pair

m.tunnelMapLock.RLock()
m.RLock()
for uuid, tunnel := range m.tunnelMap {
if tunnel.GetLastAccessedTime().Before(timeLine) {
removeIDs = append(removeIDs, pair{uuid: uuid, tunnel: tunnel})
}
}
m.tunnelMapLock.RUnlock()
m.RUnlock()

m.Lock()
for _, double := range removeIDs {
logrus.Debugf("HTTP tunnel \"%v\" has timed out.", double.uuid)
m.tunnelMapLock.Lock()
delete(m.tunnelMap, double.uuid)
m.tunnelMapLock.Unlock()

if double.tunnel != nil {
err := double.tunnel.Close()
Expand All @@ -123,92 +115,47 @@ func (m *TunnelMap) tunnelTimeoutTaskRun() {
}
}
}
m.Unlock()
return
}

/*Get *
* Returns the Tunnel having the given UUID, wrapped within a
* LastAccessedTunnel. If the no tunnel having the given UUID is
* available, null is returned.
*
* @param uuid
* The UUID of the tunnel to retrieve.
*
* @return
* The Tunnel having the given UUID, wrapped within a
* LastAccessedTunnel, if such a tunnel exists, or null if there is no
* such tunnel.
*/
// Get returns the Tunnel having the given UUID, wrapped within a LastAccessedTunnel.
func (m *TunnelMap) Get(uuid string) (tunnel *LastAccessedTunnel, ok bool) {

// Update the last access time
m.tunnelMapLock.RLock()
m.RLock()
tunnel, ok = m.tunnelMap[uuid]
m.tunnelMapLock.RUnlock()
m.RUnlock()

if ok && tunnel != nil {
tunnel.Access()
} else {
ok = false
}

// Return tunnel, if any
return

}

/*Add *
* Registers that a new connection has been established using HTTP via the
* given Tunnel.
*
* @param uuid
* The UUID of the tunnel being added (registered).
*
* @param tunnel
* The Tunnel being registered, its associated connection
* having just been established via HTTP.
*/
// Add registers that a new connection has been established using HTTP via the given Tunnel.
func (m *TunnelMap) Put(uuid string, tunnel Tunnel) {
m.Lock()
one := NewLastAccessedTunnel(tunnel)
m.tunnelMapLock.Lock()
m.tunnelMap[uuid] = &one
m.tunnelMapLock.Unlock()
m.Unlock()
}

/*Remove *
* Removes the Tunnel having the given UUID, if such a tunnel
* exists. The original tunnel is returned wrapped within a
* LastAccessedTunnel.
*
* @param uuid
* The UUID of the tunnel to remove (deregister).
*
* @return
* The Tunnel having the given UUID, if such a tunnel exists,
* wrapped within a LastAccessedTunnel, or null if no such tunnel
* exists and no removal was performed.
*/
// Remove removes the Tunnel having the given UUID, if such a tunnel exists. The original tunnel is returned.
func (m *TunnelMap) Remove(uuid string) (*LastAccessedTunnel, bool) {
m.Lock()
defer m.Unlock()

m.tunnelMapLock.RLock()
v, ok := m.tunnelMap[uuid]
m.tunnelMapLock.RUnlock()

if ok {
m.tunnelMapLock.Lock()
delete(m.tunnelMap, uuid)
m.tunnelMapLock.Unlock()
}
return v, ok
}

/*Shutdown *
* Shuts down this tunnel map, disallowing future tunnels from being
* registered and reclaiming any resources.
*/
// Shutdown stops the ticker to free up resources.
func (m *TunnelMap) Shutdown() {
for _, c := range m.executor {
c.Stop()
}
m.executor = make([]*time.Ticker, 0, 1)
m.Lock()
m.ticker.Stop()
m.Unlock()
}

0 comments on commit bacca8e

Please sign in to comment.