cloudflared/management/session.go
Devin Carr 38cd455e4d TUN-7373: Streaming logs override for same actor
To help accommodate web browser interactions with websockets, when a
streaming logs session is requested for the same actor while already
serving a session for that user in a separate request, the original
request will be closed and the new request start streaming logs
instead. This should help with rogue sessions holding on for too long
with no client on the other side (before idle timeout or connection
close).
2023-04-21 11:54:37 -07:00

89 lines
2.2 KiB
Go

package management
import (
"context"
"sync/atomic"
)
const (
// Indicates how many log messages the listener will hold before dropping.
// Provides a throttling mechanism to drop latest messages if the sender
// can't keep up with the influx of log messages.
logWindow = 30
)
// session captures a streaming logs session for a connection of an actor.
type session struct {
// Indicates if the session is streaming or not. Modifying this will affect the active session.
active atomic.Bool
// Allows the session to control the context of the underlying connection to close it out when done. Mostly
// used by the LoggerListener to close out and cleanup a session.
cancel context.CancelFunc
// Actor who started the session
actor actor
// Buffered channel that holds the recent log events
listener chan *Log
// Types of log events that this session will provide through the listener
filters *StreamingFilters
}
// NewSession creates a new session.
func newSession(size int, actor actor, cancel context.CancelFunc) *session {
s := &session{
active: atomic.Bool{},
cancel: cancel,
actor: actor,
listener: make(chan *Log, size),
filters: &StreamingFilters{},
}
return s
}
// Filters assigns the StreamingFilters to the session
func (s *session) Filters(filters *StreamingFilters) {
if filters != nil {
s.filters = filters
} else {
s.filters = &StreamingFilters{}
}
}
// Insert attempts to insert the log to the session. If the log event matches the provided session filters, it
// will be applied to the listener.
func (s *session) Insert(log *Log) {
// Level filters are optional
if s.filters.Level != nil {
if *s.filters.Level > log.Level {
return
}
}
// Event filters are optional
if len(s.filters.Events) != 0 && !contains(s.filters.Events, log.Event) {
return
}
select {
case s.listener <- log:
default:
// buffer is full, discard
}
}
// Active returns if the session is active
func (s *session) Active() bool {
return s.active.Load()
}
// Stop will halt the session
func (s *session) Stop() {
s.active.Store(false)
}
func contains(array []LogEventType, t LogEventType) bool {
for _, v := range array {
if v == t {
return true
}
}
return false
}