Skip to content

Sessions

rust-web-server ships a complete server-side session system in src/session/mod.rs. 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