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  }