package api import ( "net/http" "sync/atomic" ) // maxConcurrentSSEStreams caps the global number of in-flight SSE // connections. Each stream holds a goroutine, an event-bus subscription, and // (for log streams) a Docker daemon TCP socket; a single tab opening // thousands of EventSources would otherwise exhaust file descriptors. const maxConcurrentSSEStreams = 256 // sseGate is a counting gate that limits concurrent SSE streams. type sseGate struct { cap int64 cur atomic.Int64 } func newSSEGate(cap int) *sseGate { return &sseGate{cap: int64(cap)} } // enter reserves a slot and returns a release func, or nil if the gate is full. func (g *sseGate) enter() func() { if g.cur.Add(1) > g.cap { g.cur.Add(-1) return nil } return func() { g.cur.Add(-1) } } // acquireSSESlot is a small helper used by every SSE handler to honour the // global cap. Returns false (and writes a 503) if the cap is reached. func acquireSSESlot(w http.ResponseWriter, gate *sseGate) (release func(), ok bool) { release = gate.enter() if release == nil { respondError(w, http.StatusServiceUnavailable, "stream limit reached") return nil, false } return release, true }