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