Custom Session Handler issues
Hi all, I've been trying to track down the root of a problem I'm having with Dancer, and a custom session handler that I am using. I've been told (several IRC discussions, and scouring the Dancer docs) that unless I specifically enable sessions within dancer (either simple or YAML), that no session handling code gets initialized by default in Dancer. However I'm finding that something is preventing my CGI::Session files from being written to disk, as they are in my other non-dancer applications using code similar to the following: http://pastebin.jbldata.com/m2571aaf0 This isn't the precise code I'm trying to use in my application, but has been simplified in order to provide the same results without a lot of noise. The code defines that the CGI::Session driver writes session files to '/tmp', however no session files are written there. Running this app, you can clearly see that the custom session handler is working when you run the application from the command line (e.g, 'perl dancer-sessiontest.pl'), and visit the application in a browser using the '/' or '/dump_session' routes. If these files are not written to the directory I've specified, then likely they are being stored in memory, but how and why is it doing that if Dancer does not do any session handling by default? As you can see, I'm using a very skeletal, single file Dancer application, which does not attempt to load any of Dancer's session handling modules. Can anyone explain what is happening here, and how to get my session files handled correctly? Thanks very much for any assistance, -Bobby
On Wednesday 18 May 2011 16:37:31 J. Bobby Lopez wrote:
I've been told (several IRC discussions, and scouring the Dancer docs) that unless I specifically enable sessions within dancer (either simple or YAML), that no session handling code gets initialized by default in Dancer.
However I'm finding that something is preventing my CGI::Session files from being written to disk, as they are in my other non-dancer applications using code similar to the following:
Looking at that code, you're initialising a session at initial runtime of the app, rather than within a route handler. At runtime, there is no cookie for CGI.pm's cookie() to look at (I'm not even sure that using CGI.pm within a route handler would work, nor that trying to use CGI.pm within a Dancer app is advisable at all). So,: my $sid = $query->cookie( 'CGISESSID' ) || undef; $sid will be undef here. # Create / Load SESSION variables my $session_driver = "driver:File" if !defined( $sid ); my $session = new CGI::Session( $session_driver, $sid, { Directory => $session_dir_cache } ); What does CGI::Session do if you pass an undef session ID to the constructor? Logic would assume that it will generate a session ID, but that might not be the case. Also, you don't call $session->flush at any point to write the session contents to disc. In theory, CGI::Session will implicitly flush the session when the $session object goes out of scope (which, in your example, will only be as the Dancer app exits); that isn't always reliable, however. CGI::Session's own docs state: "As a last resort, CGI::Session will automatically call flush for you just before the program terminates or session object goes out of scope. Automatic flushing has proven to be unreliable..."
The code defines that the CGI::Session driver writes session files to '/tmp', however no session files are written there. Running this app, you can clearly see that the custom session handler is working when you run the application from the command line (e.g, 'perl dancer-sessiontest.pl'), and visit the application in a browser using the '/' or '/dump_session' routes.
You're not seeing stuff being retrieved from the session; you're seeing the session object you created at runtime being used. Different users accesssing that example app with different session ID cookies (or no session ID cookie at all) would get the same session's contents.
Can anyone explain what is happening here, and how to get my session files handled correctly?
The correct answer I should give is of course "Use Dancer's own session support, which works", but I understand you're trying to maintain compatibility with other non-Dancer apps which use CGI::Session. In that case, the proper answer is most likely to write a Dancer::Session::CGISession session engine which provides compatibility with CGI::Session-based sessions, then set that as your chosen session engine and use Dancer's own session support. Writing a session engine wrapper is quite simple - you simply inherit from Dancer::Session::Abstract and implement the required methods. You could take a look at Dancer::Session::Simple for a dead-simple example. Cheers Dave P -- David Precious ("bigpresh") http://www.preshweb.co.uk/ "Programming is like sex. One mistake and you have to support it for the rest of your life". (Michael Sinz)
On 2011-05-18, at 11:56 AM, David Precious wrote:
Also, you don't call $session->flush at any point to write the session contents to disc.
In theory, CGI::Session will implicitly flush the session when the $session object goes out of scope (which, in your example, will only be as the Dancer app exits); that isn't always reliable, however.
CGI::Session's own docs state:
"As a last resort, CGI::Session will automatically call flush for you just before the program terminates or session object goes out of scope. Automatic flushing has proven to be unreliable..."
This has bitten me on more than one occasion. If you want to be sure your changes are being saved, an explicit call to flush is a very good idea. Olaf -- Olaf Alders olaf@wundersolutions.com http://www.wundersolutions.com http://twitter.com/wundercounter 866 503 2204 (Toll free - North America) 416 944 8306 (direct)
Thanks David/Oalf, I'm working on the suggestions provided, will follow up with results. On Wed, May 18, 2011 at 12:02 PM, Olaf Alders <olaf@wundersolutions.com>wrote:
On 2011-05-18, at 11:56 AM, David Precious wrote:
Also, you don't call $session->flush at any point to write the session contents to disc.
In theory, CGI::Session will implicitly flush the session when the $session object goes out of scope (which, in your example, will only be as the Dancer app exits); that isn't always reliable, however.
CGI::Session's own docs state:
"As a last resort, CGI::Session will automatically call flush for you just before the program terminates or session object goes out of scope. Automatic flushing has proven to be unreliable..."
This has bitten me on more than one occasion. If you want to be sure your changes are being saved, an explicit call to flush is a very good idea.
Olaf -- Olaf Alders olaf@wundersolutions.com
http://www.wundersolutions.com http://twitter.com/wundercounter
866 503 2204 (Toll free - North America) 416 944 8306 (direct)
_______________________________________________ Dancer-users mailing list Dancer-users@perldancer.org http://www.backup-manager.org/cgi-bin/listinfo/dancer-users
On Wednesday 18 May 2011 16:56:39 David Precious wrote:
In that case, the proper answer is most likely to write a Dancer::Session::CGISession session engine which provides compatibility with CGI::Session-based sessions, then set that as your chosen session engine and use Dancer's own session support.
Writing a session engine wrapper is quite simple - you simply inherit from Dancer::Session::Abstract and implement the required methods. You could take a look at Dancer::Session::Simple for a dead-simple example.
In fact, take a look at https://github.com/bigpresh/Dancer-Session-CGISession which I've just started. Not yet ready to use, and I'm off out to play darts tonight so won't have it working tonight, but should be able to finish it soon, it's pretty simple stuff. Feel free to fork and submit pull requests if you want to help it along :) Cheers Dave P -- David Precious ("bigpresh") http://www.preshweb.co.uk/ "Programming is like sex. One mistake and you have to support it for the rest of your life". (Michael Sinz)
Awesome! On Wed, May 18, 2011 at 1:25 PM, David Precious <davidp@preshweb.co.uk>wrote:
On Wednesday 18 May 2011 16:56:39 David Precious wrote:
In that case, the proper answer is most likely to write a Dancer::Session::CGISession session engine which provides compatibility with CGI::Session-based sessions, then set that as your chosen session engine and use Dancer's own session support.
Writing a session engine wrapper is quite simple - you simply inherit from Dancer::Session::Abstract and implement the required methods. You could take a look at Dancer::Session::Simple for a dead-simple example.
In fact, take a look at https://github.com/bigpresh/Dancer-Session-CGISession which I've just started.
Not yet ready to use, and I'm off out to play darts tonight so won't have it working tonight, but should be able to finish it soon, it's pretty simple stuff.
Feel free to fork and submit pull requests if you want to help it along :)
Cheers
Dave P
-- David Precious ("bigpresh") http://www.preshweb.co.uk/
"Programming is like sex. One mistake and you have to support it for the rest of your life". (Michael Sinz) _______________________________________________ Dancer-users mailing list Dancer-users@perldancer.org http://www.backup-manager.org/cgi-bin/listinfo/dancer-users
I took a look at the code you have for the CGISession implementation, it looks fairly straight forward. A couple of questions though: 1. Does using Dancer::ModuleLoader->load('CGI::Session') mean that we should have access to all objects and methods exported by CGI::Session? For example, can I access CGI->self_url() somehow? 2. Would I be able to access Dancer settings and variables form a sub-module? E.g.: Is there any way to access Dancer's 'cookies' or 'session' info from a module, without having to pass them as arguments to my module? -Bobby On Wed, May 18, 2011 at 1:25 PM, David Precious <davidp@preshweb.co.uk>wrote:
On Wednesday 18 May 2011 16:56:39 David Precious wrote:
In that case, the proper answer is most likely to write a Dancer::Session::CGISession session engine which provides compatibility with CGI::Session-based sessions, then set that as your chosen session engine and use Dancer's own session support.
Writing a session engine wrapper is quite simple - you simply inherit from Dancer::Session::Abstract and implement the required methods. You could take a look at Dancer::Session::Simple for a dead-simple example.
In fact, take a look at https://github.com/bigpresh/Dancer-Session-CGISession which I've just started.
Not yet ready to use, and I'm off out to play darts tonight so won't have it working tonight, but should be able to finish it soon, it's pretty simple stuff.
Feel free to fork and submit pull requests if you want to help it along :)
Cheers
Dave P
-- David Precious ("bigpresh") http://www.preshweb.co.uk/
"Programming is like sex. One mistake and you have to support it for the rest of your life". (Michael Sinz) _______________________________________________ Dancer-users mailing list Dancer-users@perldancer.org http://www.backup-manager.org/cgi-bin/listinfo/dancer-users
On 18 May 2011 16:56, David Precious <davidp@preshweb.co.uk> wrote:
The correct answer I should give is of course "Use Dancer's own session support, which works", but I understand you're trying to maintain compatibility with other non-Dancer apps which use CGI::Session.
I looked into using CGI::Session because I don't like the way Dancer's session engine creates a session/cookie for every client unconditionally. In the end I built a custom engine based on Dancer::Session::Abstract. Al
Hi Al, Is the code somewhere on github ? On 19 May 2011 05:44, Al <calyx2011@gmail.com> wrote:
On 18 May 2011 16:56, David Precious <davidp@preshweb.co.uk> wrote:
The correct answer I should give is of course "Use Dancer's own session support, which works", but I understand you're trying to maintain compatibility with other non-Dancer apps which use CGI::Session.
I looked into using CGI::Session because I don't like the way Dancer's session engine creates a session/cookie for every client unconditionally. In the end I built a custom engine based on Dancer::Session::Abstract.
Al _______________________________________________ Dancer-users mailing list Dancer-users@perldancer.org http://www.backup-manager.org/cgi-bin/listinfo/dancer-users
I would be interested in this as well. The other problem that I haven't had time to resolve yet is that using the built-in session interface (no matter the plugin as far as I can tell) I can't set a custom session timeout (based on form input, for example). On Thu, May 19, 2011 at 12:43 AM, damien krotkine <dkrotkine@gmail.com> wrote:
Hi Al,
Is the code somewhere on github ?
On 19 May 2011 05:44, Al <calyx2011@gmail.com> wrote:
On 18 May 2011 16:56, David Precious <davidp@preshweb.co.uk> wrote:
The correct answer I should give is of course "Use Dancer's own session support, which works", but I understand you're trying to maintain compatibility with other non-Dancer apps which use CGI::Session.
I looked into using CGI::Session because I don't like the way Dancer's session engine creates a session/cookie for every client unconditionally. In the end I built a custom engine based on Dancer::Session::Abstract.
Al _______________________________________________ Dancer-users mailing list Dancer-users@perldancer.org http://www.backup-manager.org/cgi-bin/listinfo/dancer-users
_______________________________________________ Dancer-users mailing list Dancer-users@perldancer.org http://www.backup-manager.org/cgi-bin/listinfo/dancer-users
On Thursday 19 May 2011 15:32:02 Brian E. Lozier wrote:
I would be interested in this as well. The other problem that I haven't had time to resolve yet is that using the built-in session interface (no matter the plugin as far as I can tell) I can't set a custom session timeout (based on form input, for example).
You can control it with the session_expires config setting. This wasn't well documented in Dancer::Config; it is now. -- David Precious <davidp@preshweb.co.uk> (bigpresh) http://www.preshweb.co.uk/ "Programming is like sex. One mistake and you have to support it for the rest of your life". (Michael Sinz)
On Thu, May 19, 2011 at 8:14 AM, David Precious <davidp@preshweb.co.uk> wrote:
On Thursday 19 May 2011 15:32:02 Brian E. Lozier wrote:
I would be interested in this as well. The other problem that I haven't had time to resolve yet is that using the built-in session interface (no matter the plugin as far as I can tell) I can't set a custom session timeout (based on form input, for example).
You can control it with the session_expires config setting.
This wasn't well documented in Dancer::Config; it is now.
I don't see much about sessions in Dancer::Config. It just tells you you can enable sessions with the session engine. The most detail I can find is here: http://search.cpan.org/~xsawyerx/Dancer-1.3040/lib/Dancer/Session/Abstract.p... I think that's a global setting and not really what I'm talking about. If I'm completely missing something please correct me. This thread reminded me that I'd like to not have sessions enabled for all accesses, only for logged in users. However, I have been using one of the flash message plugins which require sessions for every user anyway so it's not a big deal at this point. However, this issue about sessions expiring keeps coming up for me. So pretend for a moment that I do not want a session for every user, only for logged-in users (thus, I would be creating the session). There are two things I'd like to accomplish: 1. A log in page where user doesn't check a "remember me" box. I would check user credentials, create a session, and send a cookie to the user that expires when the browser closes. This is a "session cookie" from the HTTP standpoint: http://en.wikipedia.org/wiki/HTTP_cookie#Session_cookie I would of course still keep a reasonable expire time on the server. However, in this case it's really nice to expire not only the session on the server but also the session cookie on the client so we don't leave cookies hanging around forever on client machines. 2. A log in page where user DOES check a "remember me" box. In this case I would check user credentials and then send a persistent cookie that expires at some point in the far future (like a year). http://en.wikipedia.org/wiki/HTTP_cookie#Persistent_cookie I would also set their session on the server to persist for at that amount of time. The problem (that may not be a problem if I'm misunderstanding something) is that I can't set the expire time of these session cookies on a per-request basis. Dancer creates the session cookies before I even have a chance to do anything. Dancer's session interface is nice and simple but makes it impossible to programmatically control the cookie expiration. It's set in stone (in config) when the request starts. Even if I override it in one of my routes, maybe it will work for that request, but next time the user visits some other page the cookie will be reset to whatever is in config. I have a couple of options: 1. Don't use dancer session interface for my login cookies/sessions. This will allow me to control both the server and client side expirations. 2. Never expire client side cookies and when the user doesn't check the box just expire the session sooner on the server side. If I set the server expire too short, it will annoy users (because they're still browsing in the same browser session and I expired them), but if I set it too long, it will persist across browser closes. Users expect that if they're logged into a site and they didn't check this box, when they exit the browser they will be logged out. With a persistent cookie it's impossible for me to detect whether the browser was closed between requests. I'm going to end up using option #1 because option #2 is cumbersome and unreasonable (and can be insecure, for example, on public terminals at libraries). Dancer sessions are nice but the interface to them is too limited and the fact that they are created all the time, instead of just when needed, is problematic for me. I would like one that only creates the session when session() is used and session() should take an argument for client-side cookie expiration.
-- David Precious <davidp@preshweb.co.uk> (bigpresh) http://www.preshweb.co.uk/
"Programming is like sex. One mistake and you have to support it for the rest of your life". (Michael Sinz) _______________________________________________ Dancer-users mailing list Dancer-users@perldancer.org http://www.backup-manager.org/cgi-bin/listinfo/dancer-users
On Thursday 19 May 2011 16:59:57 Brian E. Lozier wrote:
On Thu, May 19, 2011 at 8:14 AM, David Precious <davidp@preshweb.co.uk>
This wasn't well documented in Dancer::Config; it is now.
I don't see much about sessions in Dancer::Config. It just tells you you can enable sessions with the session engine.
Sorry, I wasn't very clear in my last mail - what I meant by "it is now" is "I've just improved that documentation and pushed it to GitHub" - so those improvements should be in the next stable release.
I think that's a global setting and not really what I'm talking about. If I'm completely missing something please correct me.
session_expires is indeed a global setting, but you could override it in a before handler, say: before sub { if (param->{keep_me_logged_in}) { setting session_expires => '1 year'; } else { setting session_expires => '3 hours'; }; (I think that should work) It could actually make sense to use the new hooks support to add a before_session_create hook, which would give you the opportunity to add code that runs before the session is created.
The problem (that may not be a problem if I'm misunderstanding something) is that I can't set the expire time of these session cookies on a per-request basis. Dancer creates the session cookies before I even have a chance to do anything.
The above could help, but certainly isn't perfect.
Dancer sessions are nice but the interface to them is too limited and the fact that they are created all the time, instead of just when needed, is problematic for me. I would like one that only creates the session when session() is used and session() should take an argument for client-side cookie expiration.
Agreed. session_expires: 'session' to indicate that cookies should be valid only for the browser session would seem to make sense. Also, if a session expiry time is set with session_expires, the session's expiry time should be stored in the session itself, and checked when the session is loaded. This needs a bit of work to make it more flexible, but is something I think we need to do. Cheers Dave P -- David Precious <davidp@preshweb.co.uk> (bigpresh) http://www.preshweb.co.uk/ "Programming is like sex. One mistake and you have to support it for the rest of your life". (Michael Sinz)
On 19 May 2011 05:44, Al <calyx2011@gmail.com> wrote:
On 18 May 2011 16:56, David Precious <davidp@preshweb.co.uk> wrote:
The correct answer I should give is of course "Use Dancer's own session support, which works", but I understand you're trying to maintain compatibility with other non-Dancer apps which use CGI::Session.
I looked into using CGI::Session because I don't like the way Dancer's session engine creates a session/cookie for every client unconditionally. In the end I built a custom engine based on Dancer::Session::Abstract.
On 19 May 2011 08:43, damien krotkine <dkrotkine@gmail.com> wrote:
Hi Al,
Is the code somewhere on github ?
I uploaded to pastebin. It is quite specific to my particular needs so not really suitable for distributing as modules on github. I'm sure it can be adapted without too much trouble, and might help you to write your own. For example, I like to cache the session id (sid) in the users table in my database. You could simply remove all the database specific parts from Abstract.pm and adapt it to your needs. MyApp::Session::Abstract - http://pastebin.com/RfUzQQi3 MyApp::Session::Storable - http://pastebin.com/EKnrs1mA Synopsis: use MyApp::Session::Storable; post '/login' => sub { # authenticate user if ($authenticated) { my $session = MyApp::Session::Storable->new(username => params{username}); } } before_sub { my $session = MyApp::Session::Storable->get_session; if (defined $session) { var session => $session; } } # In each route handler my $session = vars->{session}; if ($session) { ... } # The session object is a hashref, easy to add session data to it $session->{lastvisit} = localtime; # Flush it $session->store; # Delete it $session->delete;
participants (6)
-
Al -
Brian E. Lozier -
damien krotkine -
David Precious -
J. Bobby Lopez -
Olaf Alders