diff --git a/.gitignore b/.gitignore index 42d0755dd..c786504ea 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ Cargo.lock target/ guide/build/ /gh-pages +/.history *.so *.out diff --git a/src/session.rs b/src/session.rs index 935a7ce22..bb8e890d1 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,10 +1,7 @@ -use std::collections::HashMap; -use std::iter; -use std::rc::Rc; - +use std::{collections::HashMap, iter, rc::Rc}; use actix::prelude::*; use actix_service::{Service, Transform}; -use actix_session::Session; +use actix_session::{Session, SessionStatus}; use actix_utils::cloneable::CloneableService; use actix_web::cookie::{Cookie, CookieJar, Key, SameSite}; use actix_web::dev::{ServiceRequest, ServiceResponse}; @@ -14,7 +11,7 @@ use futures::future::{err, ok, Either, Future, FutureResult}; use futures::Poll; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; use redis_async::resp::RespValue; -use time::Duration; +use time::{self, Duration}; use crate::redis::{Command, RedisActor}; @@ -147,10 +144,32 @@ where }; srv.call(req).and_then(move |mut res| { - if let Some(state) = Session::get_changes(&mut res) { - Either::A(inner.update(res, state, value)) - } else { - Either::B(ok(res)) + match Session::get_changes(&mut res) { + (SessionStatus::Unchanged, _) => + Either::A(Either::A(ok(res))), + (SessionStatus::Changed, Some(state)) => + Either::B(Either::A(inner.update(res, state, value))), + (SessionStatus::Purged, Some(_)) => { + Either::B(Either::B( + inner.clear_cache(value) + .and_then(move |_| + match inner.remove_cookie(&mut res){ + Ok(_) => Either::A(ok(res)), + Err(_err) => Either::B(err( + error::ErrorInternalServerError(_err))) + }) + )) + }, + (SessionStatus::Renewed, Some(state)) => { + Either::A( + Either::B( + inner.clear_cache(value) + .and_then(move |_| + inner.update(res, state, None)) + ) + ) + }, + (_, None) => unreachable!() } }) })) @@ -289,4 +308,42 @@ impl Inner { ), } } + + /// removes cache entry + fn clear_cache(&self, value: Option) + -> impl Future { + if let Some(key) = value { + Either::A( + self.addr + .send(Command(resp_array!["DEL", key])) + .map_err(Error::from) + .and_then(|res| + match res { + // redis responds with number of deleted records + Ok(RespValue::Integer(x)) if x > 0 => Ok(()), + _ => Err(error::ErrorInternalServerError( + "failed to remove session from cache")) + } + ) + ) + } else { + Either::B(err(error::ErrorInternalServerError( + "missing session key - unexpected"))) + } + } + + /// invalidates session cookie + fn remove_cookie(&self, res: &mut ServiceResponse) + -> Result<(), Error> { + let mut cookie = Cookie::named(self.name.clone()); + cookie.set_value(""); + cookie.set_max_age(Duration::seconds(0)); + cookie.set_expires(time::now() - Duration::days(365)); + + let val = HeaderValue::from_str(&cookie.to_string()) + .map_err(|err| error::ErrorInternalServerError(err))?; + res.headers_mut().append(header::SET_COOKIE, val); + + Ok(()) + } }