1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
//! **Typed-session-axum** is a middleware providing cookie-based sessions for axum applications.
//!
//! [`SessionLayer`] provides client sessions via the [`typed_session`] crate.
//! Sessions are backed by cookies. These cookies are generated
//! when they are not found or are otherwise invalid. When a valid, known cookie
//! is received in a request, the session data is retrieved from the session store using this cookie.
//!
//! The middleware provides sessions via [`SessionHandle`]. Handlers use the
//! [`ReadableSession`](ReadableSession) and
//! [`WritableSession`](WritableSession) extractors to read
//! from and write to sessions respectively.
//!
//! The middleware expects a `SessionStoreConnection` to be present, which represents a connection
//! to a database used to store the sessions.
//! See [`SessionLayer::new`] for more details.
//!
//! # Example
//!
//! Using the middleware with axum is straightforward:
//!
//! ```rust,no_run
//! use axum::{routing::get, Router, error_handling::HandleErrorLayer, Extension};
//! use tower::ServiceBuilder;
//! use typed_session_axum::{
//! typed_session::{MemoryStore, NoLogger}, WritableSession, SessionLayer, SessionLayerError,
//! };
//! use std::fmt::Display;
//! use http::StatusCode;
//!
//! #[tokio::main]
//! async fn main() {
//! let store = MemoryStore::<i32, _>::new(); // mock database connection for debugging purposes
//! let session_layer = SessionLayer::<i32, MemoryStore<i32, NoLogger>>::new();
//!
//! async fn handler(mut session: WritableSession<i32>) {
//! *session.data_mut() = 42;
//! }
//!
//! async fn error_handler<SessionStoreConnectorError: Display, InnerError: Display>(
//! error: SessionLayerError<SessionStoreConnectorError, InnerError>
//! ) -> (StatusCode, String) {
//! (
//! StatusCode::INTERNAL_SERVER_ERROR,
//! format!("Error: {error}"),
//! )
//! }
//!
//! let app = Router::new().route("/", get(handler)).layer(
//! ServiceBuilder::new()
//! .layer(HandleErrorLayer::new(error_handler)) // handle errors
//! .layer(session_layer)
//! .layer(Extension(store)) // provide a connection to the session database
//! );
//!
//! axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
//! .serve(app.into_make_service())
//! .await
//! .unwrap();
//! }
//! ```
//!
//! This middleware may also be used as a generic [Tower](tower) middleware by making use
//! of the [`SessionHandle`] extension:
//!
//! ```rust
//! use std::convert::Infallible;
//!
//! use axum::http::header::SET_COOKIE;
//! use typed_session_axum::{typed_session::{MemoryStore, NoLogger}, SessionHandle, SessionLayer};
//! use http::{Request, Response};
//! use hyper::Body;
//! use rand::Rng;
//! use tower::{Service, ServiceBuilder, ServiceExt};
//!
//! async fn handle(request: Request<Body>) -> Result<Response<Body>, Infallible> {
//! let session_handle = request.extensions().get::<SessionHandle<()>>().unwrap();
//! let mut session = session_handle.write().await;
//! // Use the session as you'd like.
//! session.data_mut();
//!
//! Ok(Response::new(Body::empty()))
//! }
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let store = MemoryStore::<(), _>::new(); // mock database connection for debugging purposes
//! let session_layer = SessionLayer::<(), MemoryStore<(), NoLogger>>::new();
//!
//! let mut service = ServiceBuilder::new()
//! .layer(session_layer)
//! .service_fn(handle);
//!
//! let mut request = Request::builder().body(Body::empty()).unwrap();
//! request.extensions_mut().insert(store); // provide a connection to the session database
//!
//! let response = service.ready().await?.call(request).await?;
//!
//! assert!(
//! response
//! .headers()
//! .get(SET_COOKIE)
//! .unwrap()
//! .to_str()
//! .unwrap()
//! .starts_with("id=")
//! );
//!
//! # Ok(())
//! # }
//! ```
#![forbid(unsafe_code)]
#![deny(
future_incompatible,
missing_debug_implementations,
nonstandard_style,
missing_docs,
unreachable_pub,
missing_copy_implementations,
unused_qualifications
)]
pub use extractors::{ReadableSession, WritableSession};
pub use session::{SessionHandle, SessionLayer, SessionLayerError};
mod extractors;
mod session;
pub use typed_session;
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
pub struct ReadmeDoctests;