From 664f9a8b2db3ebf5f365efebb0ce42cb993486a0 Mon Sep 17 00:00:00 2001 From: Andrey Kutejko Date: Wed, 29 Jan 2020 02:26:39 +0100 Subject: [PATCH] Long lasting auto-prolonged session (#1292) Co-authored-by: Yuki Okushi --- actix-session/CHANGES.md | 1 + actix-session/src/cookie.rs | 64 +++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index f2c1a5c0b..f6753ae58 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -3,6 +3,7 @@ ## [Unreleased] - 2020-01-xx * Update the `time` dependency to 0.2.5 +* [#1292](https://github.com/actix/actix-web/pull/1292) Long lasting auto-prolonged session ## [0.3.0] - 2019-12-20 diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index bc0262935..b5297f561 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -58,6 +58,7 @@ struct CookieSessionInner { secure: bool, http_only: bool, max_age: Option, + expires_in: Option, same_site: Option, } @@ -72,6 +73,7 @@ impl CookieSessionInner { secure: true, http_only: true, max_age: None, + expires_in: None, same_site: None, } } @@ -97,6 +99,10 @@ impl CookieSessionInner { cookie.set_domain(domain.clone()); } + if let Some(expires_in) = self.expires_in { + cookie.set_expires(OffsetDateTime::now() + expires_in); + } + if let Some(max_age) = self.max_age { cookie.set_max_age(max_age); } @@ -272,6 +278,17 @@ impl CookieSession { Rc::get_mut(&mut self.0).unwrap().max_age = Some(value); self } + + /// Sets the `expires` field in the session cookie being built. + pub fn expires_in(self, seconds: i64) -> CookieSession { + self.expires_in_time(Duration::seconds(seconds)) + } + + /// Sets the `expires` field in the session cookie being built. + pub fn expires_in_time(mut self, value: Duration) -> CookieSession { + Rc::get_mut(&mut self.0).unwrap().expires_in = Some(value); + self + } } impl Transform for CookieSession @@ -324,6 +341,7 @@ where fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let inner = self.inner.clone(); let (is_new, state) = self.inner.load(&req); + let prolong_expiration = self.inner.expires_in.is_some(); Session::set_session(state.into_iter(), &mut req); let fut = self.service.call(req); @@ -335,6 +353,9 @@ where | (SessionStatus::Renewed, Some(state)) => { res.checked_expr(|res| inner.set_cookie(res, state)) } + (SessionStatus::Unchanged, Some(state)) if prolong_expiration => { + res.checked_expr(|res| inner.set_cookie(res, state)) + } (SessionStatus::Unchanged, _) => // set a new session cookie upon first request (new client) { @@ -478,4 +499,47 @@ mod tests { let body = test::read_response(&mut app, request).await; assert_eq!(body, Bytes::from_static(b"counter: 100")); } + + #[actix_rt::test] + async fn prolong_expiration() { + let mut app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false).expires_in(60)) + .service(web::resource("/").to(|ses: Session| { + async move { + let _ = ses.set("counter", 100); + "test" + } + })) + .service( + web::resource("/test/") + .to(|| async move { "no-changes-in-session" }), + ), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + let expires_1 = response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .expect("Cookie is set") + .expires() + .expect("Expiration is set"); + + actix_rt::time::delay_for(std::time::Duration::from_secs(1)).await; + + let request = test::TestRequest::with_uri("/test/").to_request(); + let response = app.call(request).await.unwrap(); + let expires_2 = response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .expect("Cookie is set") + .expires() + .expect("Expiration is set"); + + assert!(expires_2 - expires_1 >= Duration::seconds(1)); + } }