Sessions
rust-web-server ships three session store implementations in src/session/mod.rs:
| Store | Persistence | Scales horizontally |
|---|---|---|
SessionStore | In-process HashMap; lost on restart | No |
DbSessionStore | Relational database via model layer | Yes (shared DB) |
RedisSessionStore | Redis server | Yes |
All three expose the same conceptual API: create → set data → save → load → destroy. SessionStore methods return values directly; DbSessionStore and RedisSessionStore return Result because they perform I/O.
SessionStore (in-memory)
Sessions are stored in an in-memory, thread-safe map with automatic TTL-based expiry. The store is designed to live inside your application state so every handler shares the same session map without any global state.
Session store
Creating a store
use rust_web_server::session::SessionStore;
// Sessions expire after 3600 seconds (1 hour).let store = SessionStore::new(3600);SessionStore is cheaply Clone-able — all clones share the same backing map via Arc. Place one instance in your application state.
Creating a session
// Allocates a new session with a generated ID and inserts it into the store.let mut session = store.create();For public-facing services that need cryptographically unpredictable IDs, supply your own ID instead:
let id = my_csprng_hex_id(); // e.g. from `ring` or `getrandom`let mut session = store.create_with_id(id);Reading and writing session data
Session holds key/value pairs as strings. Mutate the session locally, then call save to persist changes.
session.set("user_id", "42");session.set("role", "admin");
let user_id: Option<&str> = session.get("user_id"); // Some("42")let missing: Option<&str> = session.get("absent"); // None
session.remove("role");let has_role: bool = session.contains("role"); // false
// Persist changes back to the store.store.save(&session);Loading a session
// Returns None if the session is unknown or expired.let session = store.load(&session_id);Destroying a session
store.destroy(&session_id);Call destroy_cookie to also clear the browser cookie (see below).
Purging expired sessions
Sessions are not removed automatically when they expire — they are simply invisible to load. To reclaim memory, call purge_expired() periodically:
store.purge_expired(); // removes all sessions whose TTL has elapsedUse the Scheduler to run this automatically:
use std::time::Duration;use rust_web_server::scheduler::Scheduler;
let store_clone = store.clone();Scheduler::new() .every(Duration::from_secs(3600), move || store_clone.purge_expired()) .start();Cookie helpers
Three free functions bridge the session store and the HTTP cookie layer.
session_id_from_request
Extracts the session ID from a named cookie in a request’s Cookie header.
use rust_web_server::session;
let sid: Option<String> = session::session_id_from_request(&request, "sid");Returns None if the Cookie header is absent or the named cookie is missing.
session_cookie
Builds a Set-Cookie header value with HttpOnly, SameSite=Lax, Path=/, and Max-Age.
let cookie_value = session::session_cookie(&session.id, "sid", 3600);response.headers.push(Header { name: "Set-Cookie".to_string(), value: cookie_value,});destroy_cookie
Clears the browser cookie by setting Max-Age=0.
let cookie_value = session::destroy_cookie("sid");response.headers.push(Header { name: "Set-Cookie".to_string(), value: cookie_value,});Complete login/profile example
use rust_web_server::app::App;use rust_web_server::core::New;use rust_web_server::session::{self, SessionStore};use rust_web_server::header::Header;use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
struct State { sessions: SessionStore }
let app = App::with_state(State { sessions: SessionStore::new(3600) }) .post("/login", |req, _params, _conn, state| { // Verify credentials here ... let mut sess = state.sessions.create(); sess.set("user_id", "42"); state.sessions.save(&sess);
let mut r = Response::new(); r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code; r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string(); r.headers.push(Header { name: "Set-Cookie".to_string(), value: session::session_cookie(&sess.id, "sid", 3600), }); r }) .get("/profile", |req, _params, _conn, state| { let mut r = Response::new();
let sid = match session::session_id_from_request(&req, "sid") { Some(id) => id, None => { r.status_code = *STATUS_CODE_REASON_PHRASE.n401_unauthorized.status_code; r.reason_phrase = STATUS_CODE_REASON_PHRASE.n401_unauthorized.reason_phrase.to_string(); return r; } }; let sess = match state.sessions.load(&sid) { Some(s) => s, None => { r.status_code = *STATUS_CODE_REASON_PHRASE.n401_unauthorized.status_code; r.reason_phrase = STATUS_CODE_REASON_PHRASE.n401_unauthorized.reason_phrase.to_string(); return r; } };
let _user_id = sess.get("user_id").unwrap_or("guest"); r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code; r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string(); r }) .get("/logout", |req, _params, _conn, state| { let mut r = Response::new(); if let Some(sid) = session::session_id_from_request(&req, "sid") { state.sessions.destroy(&sid); } r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code; r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string(); r.headers.push(Header { name: "Set-Cookie".to_string(), value: session::destroy_cookie("sid"), }); r });Store size
let count = store.len(); // total entries, including expired-but-not-purgedlet empty = store.is_empty(); // true when no entries existDbSessionStore (database-backed)
DbSessionStore stores sessions in a rws_sessions table managed by the model layer. Sessions survive process restarts and are visible across multiple instances that share the same database.
Setup
Requires the model-sqlite, model-postgres, or model-mysql feature. The table is created automatically on the first DbSessionStore::new call.
[dependencies]rust-web-server = { version = "17", features = ["model-sqlite"] }Basic usage
use rust_web_server::model::DbPool;use rust_web_server::session::DbSessionStore;
// Use a file or network database for real persistence;// :memory: is shown here for illustration.let pool = DbPool::memory().unwrap();let store = DbSessionStore::new(pool, 3600).unwrap();
// All methods return Result<_, DbError>let mut sess = store.create().unwrap();sess.set("user_id", "42");store.save(&sess).unwrap();
let loaded = store.load(&sess.id).unwrap().unwrap();assert_eq!(Some("42"), loaded.get("user_id"));
store.destroy(&sess.id).unwrap();Purging expired sessions
Unlike SessionStore, expired rows remain in the database until explicitly deleted:
store.purge_expired().unwrap(); // DELETE WHERE expires_at <= nowSchedule this with Scheduler:
use std::time::Duration;use rust_web_server::scheduler::Scheduler;
let store_clone = store.clone();Scheduler::new() .every(Duration::from_secs(3600), move || { let _ = store_clone.purge_expired(); }) .start();Schema
CREATE TABLE IF NOT EXISTS rws_sessions ( id TEXT PRIMARY KEY, data TEXT NOT NULL DEFAULT '', expires_at INTEGER NOT NULL);Session data is serialized as a URL-encoded string (key1=val1&key2=val2).
RedisSessionStore
RedisSessionStore stores sessions in Redis using a hand-rolled RESP v2 client — no external crate required. Sessions are keyed as rws:sess:{id} and given a Redis TTL via SET … EX, so they expire automatically without any purge_expired sweep.
Basic usage
use rust_web_server::session::RedisSessionStore;
// Connect to Redis at host:port; pass Some("password") for AUTHlet store = RedisSessionStore::new("127.0.0.1:6379", None, 3600);
let mut sess = store.create().unwrap();sess.set("role", "admin");store.save(&sess).unwrap(); // SET rws:sess:{id} "role=admin" EX 3600
let loaded = store.load(&sess.id).unwrap().unwrap();assert_eq!(Some("admin"), loaded.get("role"));
store.destroy(&sess.id).unwrap(); // DEL rws:sess:{id}Environment-variable configuration
use rust_web_server::session::RedisSessionStore;
// Reads RWS_REDIS_HOST (default 127.0.0.1), RWS_REDIS_PORT (default 6379),// RWS_REDIS_PASSWORD (optional), RWS_REDIS_TTL_SECS (default 3600)let store = RedisSessionStore::from_env();Connection behaviour
The store maintains one persistent TCP connection per RedisSessionStore instance. All clones share the same connection via Arc. The connection is established lazily on the first command and reconnects automatically if the socket is dropped.