split session management into separate module

This commit is contained in:
2020-03-31 17:54:00 -07:00
parent c206eef5fd
commit 0ba4f46c20
2 changed files with 54 additions and 45 deletions

View File

@@ -2,13 +2,13 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use cookie::{Cookie, SameSite}; use cookie::{Cookie, SameSite};
use futures::TryFutureExt;
use log::{debug, error, info, trace}; use log::{debug, error, info, trace};
use sessions::{Session, SessionStatus, Storable}; use sessions::Session;
use warp::{Filter, Rejection, Reply}; use warp::{Filter, Rejection, Reply};
use warp::http::{StatusCode, Uri}; use warp::http::{StatusCode, Uri};
const SESSION_HEADER: &str = "Spectrum-Session"; mod session;
use crate::session::{NoSession, SESSION_HEADER, SESSION_NAME, SessionPolicy, with_session};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@@ -78,44 +78,6 @@ where From: 'static,
}) })
} }
#[derive(Debug, Clone, Copy)]
enum SessionPolicy {
Existing,
AllowNew
}
const SESSION_NAME: &str = "name";
fn with_session(store: Arc<impl Storable>,
policy: SessionPolicy) -> impl Filter<Extract = (Session,),
Error = Rejection> + Clone {
warp::cookie::cookie(SESSION_HEADER)
.and_then(move |session_id: String| {
trace!("Looking up session: {}", session_id);
let store = store.clone(); // pending async reqs allowed to outlive returned Filter
async move {
let session = store.get(&session_id).await?;
session.set_id(session_id)?;
Ok(session)
}
}.map_err(|err: std::io::Error| warp::reject::custom(ServerError(Box::new(err)))))
.or_else(clarify_error::<_, warp::reject::MissingCookie, NoSession>) // Has cookies, but not session cookie
.or_else(clarify_error::<_, warp::reject::InvalidHeader, NoSession>) // No cookies at all
.and_then(move |session: Session| async move {
match (session.status(), policy) {
(Ok(SessionStatus::Existed), _ )
// Session exists, but name might not be set yet
if session.get::<String>(SESSION_NAME)
.map_or(false, |name| name.filter(|name| !name.is_empty()).is_some()) => Ok(session),
(Ok(SessionStatus::Existed), _ ) => Err(warp::reject::custom(BadName)),
(Ok(SessionStatus::Created), SessionPolicy::AllowNew) => Ok(session),
(Ok(SessionStatus::Created), _) => Err(warp::reject::custom(NoSession)),
(Err(e), _) => Err(warp::reject::custom(ServerError(Box::new(e)))),
_ => Err(warp::reject::custom(NoSession))
}
})
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct BadName; struct BadName;
impl warp::reject::Reject for BadName {} impl warp::reject::Reject for BadName {}
@@ -135,10 +97,6 @@ async fn handle_reject(err: Rejection) -> Result<Box<dyn Reply>, Rejection> {
struct ServerError(Box<dyn std::error::Error + Send + Sync>); struct ServerError(Box<dyn std::error::Error + Send + Sync>);
impl warp::reject::Reject for ServerError {} impl warp::reject::Reject for ServerError {}
#[derive(Debug, Default)]
struct NoSession;
impl warp::reject::Reject for NoSession {}
async fn handle_no_session(err: Rejection) -> Result<impl Reply, Rejection> { async fn handle_no_session(err: Rejection) -> Result<impl Reply, Rejection> {
match err.find() { match err.find() {
Some(NoSession) => { Some(NoSession) => {

51
src/session.rs Normal file
View File

@@ -0,0 +1,51 @@
use std::sync::Arc;
use futures::TryFutureExt;
use log::trace;
use sessions::{Session, SessionStatus, Storable};
use warp::{Filter, Rejection};
use crate::{BadName, clarify_error, ServerError};
pub const SESSION_HEADER: &str = "Spectrum-Session";
pub const SESSION_NAME: &str = "name";
#[derive(Debug, Clone, Copy)]
pub enum SessionPolicy {
Existing,
AllowNew
}
pub fn with_session(store: Arc<impl Storable>,
policy: SessionPolicy) -> impl Filter<Extract = (Session,),
Error = Rejection> + Clone {
warp::cookie::cookie(SESSION_HEADER)
.and_then(move |session_id: String| {
trace!("Looking up session: {}", session_id);
let store = store.clone(); // pending async reqs allowed to outlive returned Filter
async move {
let session = store.get(&session_id).await?;
session.set_id(session_id)?;
Ok(session)
}
}.map_err(|err: std::io::Error| warp::reject::custom(ServerError(Box::new(err)))))
.or_else(clarify_error::<_, warp::reject::MissingCookie, NoSession>) // Has cookies, but not session cookie
.or_else(clarify_error::<_, warp::reject::InvalidHeader, NoSession>) // No cookies at all
.and_then(move |session: Session| async move {
match (session.status(), policy) {
(Ok(SessionStatus::Existed), _ )
// Session exists, but name might not be set yet
if session.get::<String>(SESSION_NAME)
.map_or(false, |name| name.filter(|name| !name.is_empty()).is_some()) => Ok(session),
(Ok(SessionStatus::Existed), _ ) => Err(warp::reject::custom(BadName)),
(Ok(SessionStatus::Created), SessionPolicy::AllowNew) => Ok(session),
(Ok(SessionStatus::Created), _) => Err(warp::reject::custom(NoSession)),
(Err(e), _) => Err(warp::reject::custom(ServerError(Box::new(e)))),
_ => Err(warp::reject::custom(NoSession))
}
})
}
#[derive(Debug, Default)]
pub struct NoSession;
impl warp::reject::Reject for NoSession {}