Skip to content

Sessions

rust-web-server ships three session store implementations in src/session/mod.rs:

StorePersistenceScales horizontally
SessionStoreIn-process HashMap; lost on restartNo
DbSessionStoreRelational database via model layerYes (shared DB)
RedisSessionStoreRedis serverYes

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 elapsed

Use 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();

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.

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,
});

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-purged
let empty = store.is_empty(); // true when no entries exist

DbSessionStore (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 <= now

Schedule 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 AUTH
let 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.