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  }