sessions

package module
v1.5.0 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 11 Imported by: 0

README

Sessions

Sessions package for Go, use stdlib and secure cookie.

Since the experience for gorilla/sessions was s*it, we decided to write our own.

This package refers to the session module in goravel/framework.

Features

  • Encrypted session data via libtnb/securecookie (only the session ID is stored in the cookie)
  • File driver included; any backend can be plugged in through the driver.Driver interface
  • Concurrent requests to the same session merge key by key instead of overwriting each other
  • Flash data (Flash, Now, Keep, Reflash)
  • Sliding expiration: both the store timestamp and the cookie are refreshed on every request
  • Streaming-friendly middleware (SSE, chunked responses) with a buffered response writer
  • Session pooling to keep allocations low

Installation

go get github.com/libtnb/sessions

Quick start

package main

import (
	"net/http"

	"github.com/libtnb/sessions"
	"github.com/libtnb/sessions/middleware"
)

func main() {
	manager, err := sessions.NewManager(&sessions.ManagerOptions{
		Key:        "32-bytes-long-secret-key-1234567", // 32 bytes, keep it secret
		Lifetime:   120,                                // minutes, default 120
		GcInterval: 30,                                 // minutes, default 30
	})
	if err != nil {
		panic(err)
	}
	defer manager.Close()

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		s, err := manager.GetSession(r)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		views, _ := s.Get("views", 0).(int)
		s.Put("views", views+1)
	})

	http.ListenAndServe(":8080", middleware.StartSession(manager)(mux))
}

The middleware starts the session, saves it after the handler returns (merging concurrent writes), and re-sends the cookie on every successful save so its expiry slides along with the server-side lifetime.

handler := middleware.StartSessionWithConfig(manager, middleware.Config{
	Driver: "default",
	Cookie: func(c *http.Cookie) {
		c.Domain = "example.com"
		c.SameSite = http.SameSiteStrictMode
		c.Secure = true // defaults to true automatically on TLS requests
	},
})(mux)

Session API

s.Put("key", "value")          // set a value
s.Get("key", "default")        // get with optional default
s.Has("key")                   // present and non-nil
s.Exists("key")                // present, even if nil
s.Pull("key")                  // get and remove
s.Forget("key1", "key2")       // remove keys
s.Flush()                      // remove everything
s.All()                        // copy of all attributes
s.Only([]string{"k1", "k2"})   // subset of attributes

s.Flash("status", "saved!")    // visible until the end of the next request
s.Now("status", "one-shot")    // visible during the current request only
s.Keep("status")               // extend flash data one more request
s.Reflash()                    // extend all flash data one more request

s.Regenerate()                 // new session ID, keep data
s.Regenerate(true)             // new session ID, destroy old stored data
s.Invalidate()                 // flush data + new ID (use on login/logout)

Values are encoded with encoding/gob. Custom struct types stored in the session must be registered once with gob.Register.

A *Session is bound to a single request and is not safe for concurrent use by multiple goroutines; cross-request merging is handled by the manager.

Custom drivers

Implement driver.Driver and register it:

type Driver interface {
	Close() error
	Destroy(id string) error
	Gc(maxLifetime int) error
	Read(id string) (data string, found bool, err error)
	Touch(id string) (found bool, err error)
	Write(id string, data string) error
}

Read and Touch report a missing or expired session via found = false with a nil error; a non-nil error means the store itself failed. The session uses this distinction to stay safe: a missing session may be started fresh, while on a store failure the save fails loudly instead of overwriting stored data with an empty session. A session that existed at the start of the request but disappears before Save (a concurrent logout or regeneration) is never written back — Save returns sessions.ErrSessionDestroyed.

manager, _ := sessions.NewManager(&sessions.ManagerOptions{
	Key:                  "32-bytes-long-secret-key-1234567",
	DisableDefaultDriver: true, // skip the default file driver
})
_ = manager.Extend("redis", myRedisDriver)

handler := middleware.StartSession(manager, "redis")(mux)

The default file driver stores each session as a 0600 file in a dedicated per-user directory inside os.TempDir() (sessions-<uid> on Unix), writes atomically (temp file + rename), and its garbage collector only ever removes files that look like session data. Before writing it verifies the directory is a real directory owned by the current user with mode 0700 — a pre-created symlink or permissive directory in the shared temp dir is rejected. Pass a custom path via driver.NewFile(path, minutes) to store sessions elsewhere; custom paths are held to the same check.

Documentation

Index

Constants

View Source
const (
	// DefaultLifetime is used when ManagerOptions.Lifetime is not positive.
	DefaultLifetime = 120 // minutes
	// DefaultGcInterval is used when ManagerOptions.GcInterval is not positive.
	DefaultGcInterval = 30 // minutes
)

Variables

View Source
var (
	CtxKey     = "session" // session default context key
	CookieName = "session" // session default cookie name
)
View Source
var (
	ErrSessionNotFound    = errors.New("session not found")
	ErrDriverNotSet       = errors.New("driver is not set")
	ErrDriverNotSupported = errors.New("driver not supported")
	ErrDriverExists       = errors.New("driver already exists")
)

Sentinel errors returned by the Manager; test with errors.Is.

View Source
var ErrSessionDestroyed = errors.New("session destroyed concurrently")

ErrSessionDestroyed reports that a session which existed when the request started was destroyed concurrently (logout, Regenerate(true) or garbage collection in another request) before Save could persist it. The save is refused: recreating the session would resurrect an invalidated ID.

Functions

This section is empty.

Types

type Manager

type Manager struct {
	Codec      securecookie.Codec
	Lifetime   int
	GcInterval int
	// contains filtered or unexported fields
}

func NewManager

func NewManager(option *ManagerOptions) (*Manager, error)

NewManager creates a new session manager.

func (*Manager) AcquireSession

func (m *Manager) AcquireSession() *Session

AcquireSession takes a session from the pool.

func (*Manager) BuildSession

func (m *Manager) BuildSession(name string, driver ...string) (*Session, error)

BuildSession acquires a pooled session bound to the given driver. Release it with ReleaseSession when the request is done.

func (*Manager) Close added in v1.5.0

func (m *Manager) Close() error

Close stops the garbage collection timers and closes all registered drivers. It is idempotent.

func (*Manager) Extend

func (m *Manager) Extend(name string, handler driver.Driver) error

Extend registers a custom driver under the given name and starts its garbage collection timer. It is safe for concurrent use.

func (*Manager) GetSession

func (m *Manager) GetSession(r *http.Request, key ...any) (*Session, error)

GetSession returns the session stored in the request context, keyed by CtxKey unless a custom key is given.

func (*Manager) HasSession

func (m *Manager) HasSession(r *http.Request, key ...any) bool

HasSession reports whether the request context carries a session.

func (*Manager) LockSession added in v1.3.0

func (m *Manager) LockSession(id string)

LockSession locks the given session ID so that concurrent saves of the same session are serialized.

func (*Manager) Logger added in v1.5.0

func (m *Manager) Logger() *slog.Logger

Logger returns the logger the manager was configured with.

func (*Manager) ReleaseSession

func (m *Manager) ReleaseSession(session *Session)

ReleaseSession resets the session and returns it to the pool. The session must not be used afterwards.

func (*Manager) UnlockSession added in v1.3.0

func (m *Manager) UnlockSession(id string)

UnlockSession releases the lock for the given session ID.

type ManagerOptions

type ManagerOptions struct {
	// Key is the 32 bytes string used to encrypt session data.
	Key string
	// Lifetime is the session lifetime in minutes. Defaults to DefaultLifetime.
	Lifetime int
	// GcInterval is the session garbage collection interval in minutes.
	// Defaults to DefaultGcInterval.
	GcInterval int
	// DisableDefaultDriver disables the default file driver if set to true.
	DisableDefaultDriver bool
	// Logger receives background errors (garbage collection, middleware
	// saves). Defaults to slog.Default().
	Logger *slog.Logger
}

type Session

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

Session holds the data of a single session for the duration of a request. It is not safe for concurrent use by multiple goroutines.

func (*Session) All

func (s *Session) All() map[string]any

All returns a copy of the session attributes. Mutating the returned map does not affect the session; use Put and Forget to modify it.

func (*Session) Exists

func (s *Session) Exists(key string) bool

Exists reports whether the key is present, even if its value is nil.

func (*Session) Flash

func (s *Session) Flash(key string, value any) *Session

Flash stores a key/value pair that survives until the end of the next request, then is removed automatically.

func (*Session) Flush

func (s *Session) Flush() *Session

Flush removes all attributes from the session.

func (*Session) Forget

func (s *Session) Forget(keys ...string) *Session

Forget removes the given keys from the session.

func (*Session) Get

func (s *Session) Get(key string, defaultValue ...any) any

Get returns the value for key, or defaultValue (or nil) when the key is missing.

func (*Session) GetID

func (s *Session) GetID() string

GetID returns the session ID.

func (*Session) GetName

func (s *Session) GetName() string

GetName returns the session name.

func (*Session) Has

func (s *Session) Has(key string) bool

Has reports whether the key is present with a non-nil value.

func (*Session) Invalidate

func (s *Session) Invalidate() error

Invalidate flushes all attributes and regenerates the session ID, destroying the previously stored session.

func (*Session) IsDirty added in v1.3.0

func (s *Session) IsDirty() bool

IsDirty reports whether the session has unsaved changes.

func (*Session) IsStarted

func (s *Session) IsStarted() bool

IsStarted reports whether the session has been started.

func (*Session) Keep

func (s *Session) Keep(keys ...string) *Session

Keep extends the given flash keys for one more request.

func (*Session) Missing

func (s *Session) Missing(key string) bool

Missing reports whether the key is absent or nil.

func (*Session) Now

func (s *Session) Now(key string, value any) *Session

Now stores a key/value pair that is removed at the end of the current request.

func (*Session) Only

func (s *Session) Only(keys []string) map[string]any

Only returns the subset of attributes with the given keys.

func (*Session) Pull

func (s *Session) Pull(key string, def ...any) any

Pull returns the value for key and removes it from the session.

func (*Session) Put

func (s *Session) Put(key string, value any) *Session

Put stores a key/value pair in the session.

func (*Session) Reflash

func (s *Session) Reflash() *Session

Reflash extends all current flash data for one more request.

func (*Session) Regenerate

func (s *Session) Regenerate(destroy ...bool) error

Regenerate gives the session a new ID. Pass true to also destroy the previously stored session data.

func (*Session) Remove

func (s *Session) Remove(key string) any

Remove removes the key from the session and returns its previous value.

func (*Session) Save

func (s *Session) Save() error

Save persists the session to the driver and marks it as no longer started. Concurrent saves of the same session ID are merged key by key: only the keys written or forgotten during this request are applied on top of the latest stored state.

func (*Session) SetID

func (s *Session) SetID(id string) *Session

SetID sets the session ID. Invalid IDs (wrong length or characters outside [0-9A-Za-z]) are replaced with a newly generated one.

func (*Session) SetName

func (s *Session) SetName(name string) *Session

SetName sets the session name.

func (*Session) Start

func (s *Session) Start() bool

Start loads the session data from the driver. When no stored session is found, a fresh session ID is generated.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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