Search code examples
perlsessioncookiescatalyst

How can I not send a cookie when I use the Cache-Control header in Catalyst?


I'm using sessions in my Catalyst app via Session, Session::Store::DBIC, and Session::State::Cookie.

I have a few controllers and methods that send out data with a Cache-Control: public header, so its essential that the Set-Cookie: header not go out with those responses (otherwise, it'd be cached and sent to other clients, leading to possible security issues). I haven't found a good way to accomplish this.

How can I tell Session or Session::State::Cookie to not send a cookie in response to a given request?


Solution

  • Doing a little RTFS, Session.pm overrides Catalyst's finalize_headers method and sets the cookie there, through a rather deep call chain:

    finalize_header
    ⇒ _save_session_expires
    ⇒ session_expires
    ⇒ _extended_session_expires
    ⇒ extend_session_id (…::Session::State::Cookie)
    ⇒ update_session_cookie (…::Session::State::Cookie)
    

    There does not appear to be any way to flag anything in the chain to stop. The only check is a method in Cookie.pm called cookie_is_rejecting which just compares the configured cookie path to the request path.

    So, it looks like the best way to do this is to add my own override to either update_session_cookie or cookie_is_rejecting. I think I'll use cookie_is_rejecting.

    Here is the code I finally used. Note that this is rather klugy, but it works...

    package Catalyst::Plugin::Session::State::Cookie::Sanity;
    use base qw/Catalyst::Plugin::Session::State::Cookie/;
    
    use MRO::Compat;
    
    use strict;
    
    sub cookie_is_rejecting {
        my ($c, $cookie) = @_;
    
        ($c->stash->{cache_control_time} // 0) > 0
            or $c->maybe::next::method( $c, $cookie );
    }
    
    1;