session

package
v0.12.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 24, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package session provides a session manager with TTL cleanup.

Package session provides session management with pluggable storage backends.

Index

Constants

View Source
const (
	DefaultDialTimeout  = 5 * time.Second
	DefaultReadTimeout  = 3 * time.Second
	DefaultWriteTimeout = 3 * time.Second
)

Default timeouts for Redis operations.

View Source
const (
	// DefaultSessionTTL is the default time-to-live for sessions (2 hours)
	DefaultSessionTTL = 2 * time.Hour
)

Variables

View Source
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

type Factory func(id string) Session

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

func (s *LocalStorage) DeleteExpired(ctx context.Context, before time.Time) error

DeleteExpired removes all sessions that haven't been updated since the given time.

func (*LocalStorage) Load added in v0.3.8

func (s *LocalStorage) Load(_ context.Context, id string) (Session, error)

Load retrieves a session from local storage.

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.

func (*LocalStorage) Store added in v0.3.8

func (s *LocalStorage) Store(_ context.Context, session Session) error

Store saves a session to the local storage. For local storage, we store the session object directly without serialization.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager holds sessions with TTL cleanup.

func NewManager

func NewManager(ttl time.Duration, factory interface{}) *Manager

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

func NewManagerWithStorage(ttl time.Duration, factory Factory, storage Storage) *Manager

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

func (m *Manager) AddSession(session Session) error

AddSession adds an existing session to the manager. This is useful when you need to create a session with specific properties.

func (*Manager) AddWithID

func (m *Manager) AddWithID(id string) error

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

func (m *Manager) Count() int

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

func (m *Manager) Delete(id string) error

Delete removes a session by ID. Returns an error if the ID is invalid or the deletion fails.

func (*Manager) Get

func (m *Manager) Get(id string) (Session, bool)

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

func (m *Manager) Range(f func(key, value interface{}) bool)

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

func (m *Manager) Stop() error

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

func (m *Manager) UpsertSession(session Session) error

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) ID

func (s *ProxySession) ID() string

ID returns the session ID.

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

func (*RedisStorage) DeleteExpired(_ context.Context, _ time.Time) error

DeleteExpired is a no-op. Redis TTL handles key expiry natively.

func (*RedisStorage) Load added in v0.12.5

func (s *RedisStorage) Load(ctx context.Context, id string) (Session, error)

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.

func (*RedisStorage) Store added in v0.12.5

func (s *RedisStorage) Store(ctx context.Context, session Session) error

Store serializes the session and persists it with SET … EX, refreshing the TTL on every call.

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

type SentinelConfig struct {
	MasterName    string
	SentinelAddrs []string
}

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

func NewStreamableSession(id string) Session

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

Type identifies this as a streamable session

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL