github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/server/idle/tracker.go (about) 1 package idle 2 3 import ( 4 "net" 5 "net/http" 6 "sync" 7 "time" 8 9 "github.com/sirupsen/logrus" 10 ) 11 12 // Tracker holds the state for the server's idle tracking 13 type Tracker struct { 14 // Duration is the API idle window 15 Duration time.Duration 16 hijacked int // count of active connections managed by handlers 17 managed map[net.Conn]struct{} // set of active connections managed by http package 18 mux sync.Mutex // protect managed map 19 timer *time.Timer 20 total int // total number of connections made to this server instance 21 } 22 23 // NewTracker creates and initializes a new Tracker object 24 // For best behavior, duration should be 2x http idle connection timeout 25 func NewTracker(idle time.Duration) *Tracker { 26 return &Tracker{ 27 managed: make(map[net.Conn]struct{}), 28 Duration: idle, 29 timer: time.NewTimer(idle), 30 } 31 } 32 33 // ConnState is called on HTTP connection state changes. 34 // - Once StateHijacked, StateClose is _NOT_ called on that connection 35 // - There are two "idle" timeouts, the http idle connection (not to be confused with the TCP/IP idle socket timeout) 36 // and the API idle window. The caller should set the http idle timeout to 2x the time provided to NewTacker() which 37 // is the API idle window. 38 func (t *Tracker) ConnState(conn net.Conn, state http.ConnState) { 39 t.mux.Lock() 40 defer t.mux.Unlock() 41 42 logrus.Debugf("IdleTracker %p:%v %dm+%dh/%dt connection(s)", conn, state, len(t.managed), t.hijacked, t.TotalConnections()) 43 switch state { 44 case http.StateNew: 45 t.total++ 46 case http.StateActive: 47 // stop the API timer when the server transitions any connection to an "active" state 48 t.managed[conn] = struct{}{} 49 t.timer.Stop() 50 case http.StateHijacked: 51 // hijacked connections should call Close() when finished. 52 // Note: If a handler hijack's a connection and then doesn't Close() it, 53 // the API timer will not fire and the server will _NOT_ timeout. 54 delete(t.managed, conn) 55 t.hijacked++ 56 case http.StateIdle: 57 // When any connection goes into the http idle state, we know: 58 // - we have an active connection 59 // - the API timer should not be counting down (See case StateNew/StateActive) 60 break 61 case http.StateClosed: 62 oldActive := t.ActiveConnections() 63 64 // Either the server or a hijacking handler has closed the http connection to a client 65 if conn == nil { 66 t.hijacked-- // guarded by t.mux above 67 } else { 68 if _, found := t.managed[conn]; found { 69 delete(t.managed, conn) 70 } else { 71 logrus.Warnf("IdleTracker %p: StateClosed transition by un-managed connection", conn) 72 } 73 } 74 75 // Transitioned from any "active" connection to no connections 76 if oldActive > 0 && t.ActiveConnections() == 0 { 77 t.timer.Stop() // See library source for Reset() issues and why they are not fixed 78 t.timer.Reset(t.Duration) // Restart the API window timer 79 } 80 } 81 } 82 83 // Close is used to update Tracker that a StateHijacked connection has been closed by handler (StateClosed) 84 func (t *Tracker) Close() { 85 t.ConnState(nil, http.StateClosed) 86 } 87 88 // ActiveConnections returns the number of current managed or StateHijacked connections 89 func (t *Tracker) ActiveConnections() int { 90 return len(t.managed) + t.hijacked 91 } 92 93 // TotalConnections returns total number of connections made to this instance of the service 94 func (t *Tracker) TotalConnections() int { 95 return t.total 96 } 97 98 // Done is called when idle timer has expired 99 func (t *Tracker) Done() <-chan time.Time { 100 return t.timer.C 101 }