Source File
stats.go
Belonging Package
go.pact.im/x/httptrack
package httptrack
import (
)
// counterSize is the count of [http.ConnState] states that are used for
// counter-based statistics.
const counterSize = http.StateClosed + 1 // all states
// gaugeSize is the count of [http.ConnState] states that are used for
// gauge-based statistics.
const gaugeSize = http.StateIdle + 1 // non-terminal states
// Stats holds a snapshot of HTTP connection state statistics.
type Stats struct {
counter [counterSize]uint64
gauge [gaugeSize]uint
}
// Accepted returns the number of connections currently in the [http.StateNew]
// state.
//
// A non-zero value typically indicates that there are accepted connections
// which have not yet sent a complete HTTP request. This may occur naturally
// under normal traffic, but a persistently high number of connections in this
// state can suggest:
//
// - Slow or high-latency clients
// - Clients intentionally delaying requests (e.g. denial-of-service attack)
// - Server under heavy load or accepting connections faster than it can
// process them
func ( *Stats) () uint {
return .gauge[http.StateNew]
}
// Active returns the number of connections currently in the [http.StateActive]
// state.
//
// In HTTP/1.x, this usually means that a request is currently being processed.
//
// In HTTP/2, this state indicates that the connection has at least one open
// stream. However, the connection may briefly enter the Active state after
// being accepted as part of reading the HTTP/2 connection preface, before
// transitioning to Idle state. Therefore, a connection in the Active state
// is not guaranteed to be actively handling a request.
func ( *Stats) () uint {
return .gauge[http.StateActive]
}
// Idle returns the number of connections currently in the [http.StateIdle]
// state.
//
// In HTTP/1.x, Idle connections are typically those that have completed
// a request and are being kept alive. A non-zero Idle count is normal in
// servers that support HTTP keep-alive.
//
// In HTTP/2, this state indicates that the connection has no open streams.
// A newly established HTTP/2 connection enters the Idle state after reading
// the connection preface.
func ( *Stats) () uint {
return .gauge[http.StateIdle]
}
// AcceptedTotal returns the total number of connections ever seen in the
// [http.StateNew] state.
//
// This is a monotonically increasing counter that reflects the cumulative
// number of accepted connections. It includes all connections, regardless of
// whether they were later closed, hijacked, or reused.
func ( *Stats) () uint64 {
return .counter[http.StateNew]
}
// ActiveTotal returns the total number of times connections have transitioned
// into the [http.StateActive] state.
//
// This counter increases every time a connection becomes active. It does not
// represent the number of requests, since a single connection may enter Active
// state for multiple requests (e.g. HTTP/2 streams).
func ( *Stats) () uint64 {
return .counter[http.StateActive]
}
// IdleTotal returns the total number of times connections have entered the
// [http.StateIdle] state.
func ( *Stats) () uint64 {
return .counter[http.StateIdle]
}
// HijackedTotal returns the number of connections that have been hijacked via
// [http.Hijacker].
//
// A hijacked connection is taken over by the application (e.g. for WebSocket
// upgrade) and is no longer managed by the HTTP server.
func ( *Stats) () uint64 {
return .counter[http.StateHijacked]
}
// ClosedTotal returns the number of connections that have been closed.
//
// A non-zero Closed count indicates connections that have been fully closed
// by either the server or the client, including normal connection termination
// and error scenarios.
//
// High numbers in Closed are typical over time, but a sudden spike may
// indicate:
//
// - Mass client disconnects
// - Server-side timeouts or errors
// - Deployment cycles or load balancer resets
func ( *Stats) () uint64 {
return .counter[http.StateClosed]
}
// StatsTracker is a [Tracker] implementation that keeps counts of HTTP
// connections in each state. It can be used for monitoring connections
// and their lifecycle.
type StatsTracker struct {
mu sync.Mutex
conns map[net.Conn]http.ConnState
stats Stats
}
// Track records the transition of a connection’s state. It updates internal
// statistics and tracks the current state of each connection.
//
// The dynamic type of the provided net.Conn must be comparable (i.e. usable as
// a map key). Most standard connection types (such as tls.Conn pointer) meet
// this requirement. Passing a non-comparable type will cause a runtime panic.
func ( *StatsTracker) ( net.Conn, http.ConnState) {
.mu.Lock()
defer .mu.Unlock()
.stats.counter[]++
if , := .conns[]; {
.stats.gauge[]--
}
switch {
case http.StateHijacked, http.StateClosed:
delete(.conns, )
default: // non-terminal states
if .conns == nil {
.conns = make(map[net.Conn]http.ConnState)
}
.conns[] =
.stats.gauge[]++
}
}
// Stats returns a snapshot of connection statistics.
func ( *StatsTracker) () Stats {
.mu.Lock()
defer .mu.Unlock()
return .stats
}
// Connections returns a snapshot of connection states.
func ( *StatsTracker) () map[net.Conn]http.ConnState {
.mu.Lock()
defer .mu.Unlock()
// Note that we return the original map instead of a cloned map. That
// is, as a side effect of this method, the internal map is shrunk.
// See https://go.dev/issue/20135 and https://antonz.org/go-map-shrink
var map[net.Conn]http.ConnState
.conns, = maps.Clone(.conns), .conns
return
}
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64)