Documentation
¶
Overview ¶
Package session provides a session manager with TTL cleanup.
Package session provides session management with pluggable storage backends.
Index ¶
- Constants
- Variables
- type Factory
- type LegacyFactory
- type LocalStorage
- func (s *LocalStorage) Close() error
- func (s *LocalStorage) Count() int
- func (s *LocalStorage) Delete(_ context.Context, id string) error
- func (s *LocalStorage) DeleteExpired(ctx context.Context, before time.Time) error
- func (s *LocalStorage) Load(_ context.Context, id string) (Session, error)
- func (s *LocalStorage) Range(f func(key, value interface{}) bool)
- func (s *LocalStorage) Store(_ context.Context, session Session) error
- type Manager
- func NewManager(ttl time.Duration, factory interface{}) *Manager
- func NewManagerWithRedis(ctx context.Context, ttl time.Duration, factory Factory, cfg RedisConfig) (*Manager, error)
- func NewManagerWithStorage(ttl time.Duration, factory Factory, storage Storage) *Manager
- func NewTypedManager(ttl time.Duration, sessionType SessionType) *Manager
- func (m *Manager) AddSession(session Session) error
- func (m *Manager) AddWithID(id string) error
- func (m *Manager) Count() int
- func (m *Manager) Delete(id string) error
- func (m *Manager) Get(id string) (Session, bool)
- func (m *Manager) Range(f func(key, value interface{}) bool)
- func (m *Manager) Stop() error
- func (m *Manager) UpsertSession(session Session) error
- type ProxySession
- func (s *ProxySession) CreatedAt() time.Time
- func (s *ProxySession) DeleteMetadata(key string)
- func (s *ProxySession) GetData() interface{}
- func (s *ProxySession) GetMetadata() map[string]string
- func (s *ProxySession) GetMetadataValue(key string) (string, bool)
- func (s *ProxySession) ID() string
- func (s *ProxySession) SetData(data interface{})
- func (s *ProxySession) SetMetadata(key, value string)
- func (s *ProxySession) Touch()
- func (s *ProxySession) Type() SessionType
- func (s *ProxySession) UpdatedAt() time.Time
- type RedisConfig
- type RedisStorage
- func (s *RedisStorage) Close() error
- func (s *RedisStorage) Delete(ctx context.Context, id string) error
- func (*RedisStorage) DeleteExpired(_ context.Context, _ time.Time) error
- func (s *RedisStorage) Load(ctx context.Context, id string) (Session, error)
- func (s *RedisStorage) Store(ctx context.Context, session Session) error
- type RedisTLSConfig
- type SSESession
- func (s *SSESession) Disconnect()
- func (s *SSESession) GetClientInfo() *ssecommon.SSEClient
- func (s *SSESession) GetConnectionStatus() bool
- func (s *SSESession) GetCreatedAt() time.Time
- func (s *SSESession) SendMessage(msg string) error
- func (s *SSESession) SetClientInfo(client *ssecommon.SSEClient)
- type SentinelConfig
- type Session
- type SessionType
- type Storage
- type StreamableSession
Constants ¶
const ( DefaultDialTimeout = 5 * time.Second DefaultReadTimeout = 3 * time.Second DefaultWriteTimeout = 3 * time.Second )
Default timeouts for Redis operations.
const ( // DefaultSessionTTL is the default time-to-live for sessions (2 hours) DefaultSessionTTL = 2 * time.Hour )
Variables ¶
var ( // ErrSessionDisconnected is returned when trying to send to a disconnected session ErrSessionDisconnected = httperr.WithCode( errors.New("session is disconnected"), http.StatusServiceUnavailable, ) // ErrMessageChannelFull is returned when the message channel is full ErrMessageChannelFull = httperr.WithCode( errors.New("message channel is full"), http.StatusServiceUnavailable, ) // ErrSessionNotFound is returned when a session cannot be found ErrSessionNotFound = httperr.WithCode( errors.New("session not found"), http.StatusNotFound, ) // ErrSessionAlreadyExists is returned when trying to create a session with an existing ID ErrSessionAlreadyExists = httperr.WithCode( errors.New("session already exists"), http.StatusConflict, ) // ErrInvalidSessionType is returned when an invalid session type is provided ErrInvalidSessionType = httperr.WithCode( errors.New("invalid session type"), http.StatusBadRequest, ) )
Common session errors
Functions ¶
This section is empty.
Types ¶
type Factory ¶
Factory defines a function type for creating new sessions. It now returns the Session interface to support different session types.
type LegacyFactory ¶ added in v0.2.17
type LegacyFactory func(id string) *ProxySession
LegacyFactory is the old factory type for backward compatibility
type LocalStorage ¶ added in v0.3.8
type LocalStorage struct {
// contains filtered or unexported fields
}
LocalStorage implements the Storage interface using an in-memory sync.Map. This is the default storage backend for single-instance deployments.
func NewLocalStorage ¶ added in v0.3.8
func NewLocalStorage() *LocalStorage
NewLocalStorage creates a new local in-memory storage backend.
func (*LocalStorage) Close ¶ added in v0.3.8
func (s *LocalStorage) Close() error
Close clears all sessions from local storage.
func (*LocalStorage) Count ¶ added in v0.3.8
func (s *LocalStorage) Count() int
Count returns the number of sessions in storage. This is a helper method not part of the Storage interface.
func (*LocalStorage) Delete ¶ added in v0.3.8
func (s *LocalStorage) Delete(_ context.Context, id string) error
Delete removes a session from local storage.
func (*LocalStorage) DeleteExpired ¶ added in v0.3.8
DeleteExpired removes all sessions that haven't been updated since the given time.
func (*LocalStorage) Range ¶ added in v0.3.8
func (s *LocalStorage) Range(f func(key, value interface{}) bool)
Range iterates over all sessions in storage. This is a helper method not part of the Storage interface.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager holds sessions with TTL cleanup.
func NewManager ¶
NewManager creates a session manager with TTL and starts cleanup worker. It accepts either the new Factory or the legacy factory for backward compatibility.
func NewManagerWithRedis ¶ added in v0.12.5
func NewManagerWithRedis(ctx context.Context, ttl time.Duration, factory Factory, cfg RedisConfig) (*Manager, error)
NewManagerWithRedis creates a session manager backed by Redis. ctx is used for the initial Ping during construction and should carry any deadline appropriate for the connection attempt (e.g. a startup timeout). cfg supplies the Redis connection configuration; ttl is applied as both the manager's cleanup interval and the Redis key TTL. Returns an error if the Redis client cannot be constructed (e.g. invalid config or TLS error). Callers that do not require Redis should use NewManager or NewTypedManager instead.
func NewManagerWithStorage ¶ added in v0.3.8
NewManagerWithStorage creates a session manager with a custom storage backend.
func NewTypedManager ¶ added in v0.2.17
func NewTypedManager(ttl time.Duration, sessionType SessionType) *Manager
NewTypedManager creates a session manager for a specific session type.
func (*Manager) AddSession ¶ added in v0.2.17
AddSession adds an existing session to the manager. This is useful when you need to create a session with specific properties.
func (*Manager) AddWithID ¶
AddWithID creates (and adds) a new session with the provided ID. Returns error if ID is empty, not a valid UUID, or already exists.
func (*Manager) Count ¶ added in v0.2.17
Count returns the number of active sessions.
Note: This method only works with LocalStorage backend and returns 0 for other storage backends. Count is not part of the Storage interface because it's not feasible for distributed storage backends like Redis where counting all keys can be prohibitively expensive.
For distributed storage, consider maintaining a counter or using approximate count mechanisms provided by the storage backend.
func (*Manager) Delete ¶
Delete removes a session by ID. Returns an error if the ID is invalid or the deletion fails.
func (*Manager) Get ¶
Get retrieves a session by ID. Returns (session, true) if found, and also updates its UpdatedAt timestamp.
func (*Manager) Range ¶ added in v0.2.17
Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration.
Note: This method only works with LocalStorage backend. It will silently do nothing with other storage backends. Range is not part of the Storage interface because it's not feasible for distributed storage backends like Redis where iterating all keys can be prohibitively expensive or impractical.
For distributed storage, consider using more targeted queries or maintaining a separate index of session IDs.
func (*Manager) Stop ¶
Stop stops the cleanup worker and closes the storage backend. Returns an error if closing the storage backend fails.
func (*Manager) UpsertSession ¶ added in v0.11.0
UpsertSession inserts or updates a session in storage, replacing any existing session with the same ID. Used by SessionManager to replace placeholder sessions with fully-formed MultiSession objects after phase-2 construction.
type ProxySession ¶
type ProxySession struct {
// contains filtered or unexported fields
}
ProxySession implements the Session interface for proxy sessions. It now includes support for session types, metadata, and custom data.
func NewProxySession ¶
func NewProxySession(id string) *ProxySession
NewProxySession creates a new ProxySession with the given ID. It defaults to SessionTypeMCP for backward compatibility.
func NewTypedProxySession ¶ added in v0.2.17
func NewTypedProxySession(id string, sessType SessionType) *ProxySession
NewTypedProxySession creates a new ProxySession with the given ID and type.
func (*ProxySession) CreatedAt ¶
func (s *ProxySession) CreatedAt() time.Time
CreatedAt returns the creation time of the session.
func (*ProxySession) DeleteMetadata ¶ added in v0.2.17
func (s *ProxySession) DeleteMetadata(key string)
DeleteMetadata removes a metadata key.
func (*ProxySession) GetData ¶ added in v0.2.17
func (s *ProxySession) GetData() interface{}
GetData returns the session-specific data.
func (*ProxySession) GetMetadata ¶ added in v0.2.17
func (s *ProxySession) GetMetadata() map[string]string
GetMetadata returns all metadata as a map.
func (*ProxySession) GetMetadataValue ¶ added in v0.2.17
func (s *ProxySession) GetMetadataValue(key string) (string, bool)
GetMetadataValue gets a specific metadata value.
func (*ProxySession) SetData ¶ added in v0.2.17
func (s *ProxySession) SetData(data interface{})
SetData sets the session-specific data.
func (*ProxySession) SetMetadata ¶ added in v0.2.17
func (s *ProxySession) SetMetadata(key, value string)
SetMetadata sets a metadata key-value pair.
func (*ProxySession) Touch ¶
func (s *ProxySession) Touch()
Touch updates the session's last updated time to the current time.
func (*ProxySession) Type ¶ added in v0.2.17
func (s *ProxySession) Type() SessionType
Type returns the session type.
func (*ProxySession) UpdatedAt ¶
func (s *ProxySession) UpdatedAt() time.Time
UpdatedAt returns the last updated time of the session.
type RedisConfig ¶ added in v0.12.5
type RedisConfig struct {
// Addr is the Redis server address for standalone mode (e.g., "host:port").
Addr string
// SentinelConfig, when non-nil, activates Sentinel mode. Mutually exclusive with Addr.
SentinelConfig *SentinelConfig
// Username is the Redis ACL username (Redis 6.0+). When non-empty, both
// Username and Password are sent as ACL credentials (AUTH username password).
// Leave empty to authenticate as the default user (legacy AUTH password).
Username string
// Password is the Redis AUTH password. Used with Username for ACL auth,
// or alone for legacy AUTH with the default user.
Password string //nolint:gosec // G101: not a hardcoded credential
// DB is the Redis database index.
DB int
// KeyPrefix namespaces all session keys (e.g., "thv:vmcp:session:").
KeyPrefix string
// DialTimeout is the timeout for establishing a connection. Defaults to 5s.
DialTimeout time.Duration
// ReadTimeout is the timeout for read operations. Defaults to 3s.
ReadTimeout time.Duration
// WriteTimeout is the timeout for write operations. Defaults to 3s.
WriteTimeout time.Duration
// TLS configures TLS for Redis connections. nil means plaintext.
TLS *RedisTLSConfig
}
RedisConfig configures the Redis storage backend for session storage. Addr is used for standalone; SentinelConfig activates Sentinel mode (mutually exclusive).
type RedisStorage ¶ added in v0.12.5
type RedisStorage struct {
// contains filtered or unexported fields
}
RedisStorage implements the Storage interface backed by Redis.
func NewRedisStorage ¶ added in v0.12.5
func NewRedisStorage(ctx context.Context, cfg RedisConfig, ttl time.Duration) (*RedisStorage, error)
NewRedisStorage constructs a RedisStorage from a RedisConfig. ttl is the expiry applied to every key on Store and refreshed on every Load (sliding window). Because TTL is sliding, sessions remain valid indefinitely while actively used; revocation requires an explicit Delete call. There is no absolute maximum session lifetime.
func (*RedisStorage) Close ¶ added in v0.12.5
func (s *RedisStorage) Close() error
Close closes the underlying Redis client connection.
func (*RedisStorage) Delete ¶ added in v0.12.5
func (s *RedisStorage) Delete(ctx context.Context, id string) error
Delete removes the Redis key. A missing key is not an error.
func (*RedisStorage) DeleteExpired ¶ added in v0.12.5
DeleteExpired is a no-op. Redis TTL handles key expiry natively.
func (*RedisStorage) Load ¶ added in v0.12.5
Load retrieves a session by ID. Returns ErrSessionNotFound when the key does not exist. The Redis eviction TTL is refreshed atomically via GETEX on every read so that active sessions are not evicted between accesses. The session's UpdatedAt timestamp is not modified.
Lifetime note: this implements a sliding-window TTL. A session accessed at least once per TTL window will never expire and can live indefinitely while the client keeps making requests. Session revocation therefore depends entirely on explicit Delete calls (e.g. on logout or token invalidation); there is no absolute maximum session lifetime enforced here. If a hard cap is required in future, a MaxLifetime field checked against the session's CreatedAt would be the path forward.
type RedisTLSConfig ¶ added in v0.12.5
type RedisTLSConfig struct {
// InsecureSkipVerify skips TLS certificate verification.
InsecureSkipVerify bool
// CACert is the PEM-encoded CA certificate for verifying the server.
// When nil, the system root CAs are used.
CACert []byte
}
RedisTLSConfig holds TLS configuration for Redis connections. Presence of this struct enables TLS for the connection.
type SSESession ¶ added in v0.2.17
type SSESession struct {
*ProxySession
// SSE-specific fields
MessageCh chan string
ClientInfo *ssecommon.SSEClient
IsConnected bool
}
SSESession represents an SSE (Server-Sent Events) session. It embeds ProxySession and adds SSE-specific functionality.
func NewSSESession ¶ added in v0.2.17
func NewSSESession(id string) *SSESession
NewSSESession creates a new SSE session with the given ID.
func NewSSESessionWithClient ¶ added in v0.2.17
func NewSSESessionWithClient(id string, client *ssecommon.SSEClient) *SSESession
NewSSESessionWithClient creates a new SSE session with the given ID and client info.
func (*SSESession) Disconnect ¶ added in v0.2.17
func (s *SSESession) Disconnect()
Disconnect marks the session as disconnected and closes the message channel.
func (*SSESession) GetClientInfo ¶ added in v0.2.17
func (s *SSESession) GetClientInfo() *ssecommon.SSEClient
GetClientInfo returns the SSE client information.
func (*SSESession) GetConnectionStatus ¶ added in v0.2.17
func (s *SSESession) GetConnectionStatus() bool
GetConnectionStatus returns whether the SSE session is connected.
func (*SSESession) GetCreatedAt ¶ added in v0.2.17
func (s *SSESession) GetCreatedAt() time.Time
GetCreatedAt returns when the SSE session was created. This is useful for tracking connection duration.
func (*SSESession) SendMessage ¶ added in v0.2.17
func (s *SSESession) SendMessage(msg string) error
SendMessage sends a message to the SSE client if connected.
func (*SSESession) SetClientInfo ¶ added in v0.2.17
func (s *SSESession) SetClientInfo(client *ssecommon.SSEClient)
SetClientInfo sets the SSE client information.
type SentinelConfig ¶ added in v0.12.5
SentinelConfig contains Redis Sentinel configuration.
type Session ¶
type Session interface {
ID() string
Type() SessionType
CreatedAt() time.Time
UpdatedAt() time.Time
Touch()
// Data and metadata methods
GetData() interface{}
SetData(data interface{})
GetMetadata() map[string]string
SetMetadata(key, value string)
}
Session interface defines the contract for all session types
func NewStreamableSession ¶ added in v0.2.17
NewStreamableSession constructs a new streamable session.
type SessionType ¶ added in v0.2.17
type SessionType string
SessionType represents the type of session
const ( // SessionTypeMCP represents a standard MCP session SessionTypeMCP SessionType = "mcp" // SessionTypeSSE represents an SSE (Server-Sent Events) session SessionTypeSSE SessionType = "sse" // SessionTypeStreamable represents a streamable HTTP session SessionTypeStreamable SessionType = "streamable" )
type Storage ¶ added in v0.3.8
type Storage interface {
// Store creates or updates a session in the storage backend.
// If the session already exists, it will be overwritten.
Store(ctx context.Context, session Session) error
// Load retrieves a session by ID from the storage backend.
// Returns ErrSessionNotFound if the session doesn't exist.
//
// Implementations may refresh the backend's eviction TTL on every Load (e.g. Redis
// GETEX) to prevent active sessions from expiring between reads, because Manager.Get
// calls Touch on the returned object but does not call Store. This TTL refresh is a
// backend-level eviction concern and is distinct from the session's application-level
// UpdatedAt timestamp, which Load must NOT update.
Load(ctx context.Context, id string) (Session, error)
// Delete removes a session from the storage backend.
// It is not an error if the session doesn't exist.
Delete(ctx context.Context, id string) error
// DeleteExpired removes all sessions that haven't been updated since the given time.
// This is used by the cleanup routine to remove stale sessions.
DeleteExpired(ctx context.Context, before time.Time) error
// Close performs cleanup of the storage backend.
// For local storage, this clears all sessions. For remote storage, it closes connections.
Close() error
}
Storage defines the minimal interface for session storage backends. This interface is designed to be simple and efficient, supporting both local in-memory storage and distributed storage backends like Redis/Valkey.
type StreamableSession ¶ added in v0.2.17
type StreamableSession struct {
*ProxySession
// contains filtered or unexported fields
}
StreamableSession represents a Streamable HTTP session
func (*StreamableSession) Disconnect ¶ added in v0.2.17
func (s *StreamableSession) Disconnect()
Disconnect marks session as disconnected.
func (*StreamableSession) GetData ¶ added in v0.2.17
func (*StreamableSession) GetData() interface{}
GetData returns nil for StreamableSession.
func (*StreamableSession) SetData ¶ added in v0.2.17
func (*StreamableSession) SetData(interface{})
SetData is a no-op for StreamableSession.
func (*StreamableSession) Type ¶ added in v0.2.17
func (*StreamableSession) Type() SessionType
Type identifies this as a streamable session