Repository: actix/actix-web
File: actix-session/src/cookie.rs (Rust)

Code before:
    inner: Rc<CookieSessionInner>,
}

impl<S, B: 'static> Service for CookieSessionMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
    S::Future: 'static,
    S::Error: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = S::Error;
    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.service.poll_ready()
    }

    fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
        let inner = self.inner.clone();
        let state = self.inner.load(&req);
        Session::set_session(state.into_iter(), &mut req);

        Box::new(self.service.call(req).map(move |mut res| {
            match Session::get_changes(&mut res) {
                (SessionStatus::Changed, Some(state)) =>
                    res.checked_expr(|res| inner.set_cookie(res, state)),
                _ => res
            }
        }))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::{test, web, App};
    use bytes::Bytes;

    #[test]
    fn cookie_session() {
        let mut app = test::init_service(
            App::new()
                .wrap(CookieSession::signed(&[0; 32]).secure(false))
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                })),
        );

        let request = test::TestRequest::get().to_request();

Review comment (suggestion):
i think we should update cookie on `change` and `renew`, and delete cookie on `purge`

Code after:
    type Transform = CookieSessionMiddleware<S>;
    type Future = FutureResult<Self::Transform, Self::InitError>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(CookieSessionMiddleware {
            service,
            inner: self.0.clone(),
        })
    }
}

/// Cookie session middleware
pub struct CookieSessionMiddleware<S> {
    service: S,
    inner: Rc<CookieSessionInner>,
}

impl<S, B: 'static> Service for CookieSessionMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
    S::Future: 'static,
    S::Error: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = S::Error;
    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.service.poll_ready()
    }

    /// On first request, a new session cookie is returned in response, regardless
    /// of whether any session state is set.  With subsequent requests, if the 
    /// session state changes, then set-cookie is returned in response.  As
    /// a user logs out, call session.purge() to set SessionStatus accordingly
    /// and this will trigger removal of the session cookie in the response.
    fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
        let inner = self.inner.clone();
        let (is_new, state) = self.inner.load(&req);
        Session::set_session(state.into_iter(), &mut req);

        Box::new(self.service.call(req).map(move |mut res| {
            match Session::get_changes(&mut res) {
                (SessionStatus::Changed, Some(state))
                 | (SessionStatus::Renewed, Some(state)) => 
                    res.checked_expr(|res| inner.set_cookie(res, state)),
                (SessionStatus::Unchanged, _) =>
                    // set a new session cookie upon first request (new client)
                    if is_new {
                        let state: HashMap<String, String> = HashMap::new();