CHP-11>

No, we are not going teach you how to make a delicious tofu and soybean stew. But this is almost as good. This chapter shows how to do some common Mason tasks, some of them with more than one implementation.


Sessions

CHP-11-SECT-1>

For many of our sessions> session examples, we will be using the Apache::Session Apache::Session module> modules;Apache::Session> module. Despite its name, this module doesn't actually require mod_perl or Apache, though that is the context in which it was born and in which it's most often used. It implements a simple tied hash interface to a persistent object.N<If you are not familiar with Perl's tied variable feature, we suggest reading the perltie manpages (perldoc perltie).> It has one major gotcha: you must make sure that the session object gets cleaned up properly (usually by letting it go out of scope), so that it will be written to disk after each access.

Without Touching httpd.conf

CHP-11-SECT-1.1> httpd.conf files>

Here is an example that doesn't involve changing any of your Apache configuration settings. The following code should be placed in a top-level autohandler. Any component that needs to use the session will have to inherit from this component, either directly or via a longer inheritance chain.

It uses cookies;storing sessions with> > sessions;storing> cookies to store the session.

  <%once>
   use Apache::Cookie;
   use Apache::Session::File;
  </%once>
  <%init>
   my %c = Apache::Cookie->fetch;
   my $session_id =
       exists $c{masonbook_session} ? $c{masonbook_session}->value : undef;

First, it loads the necessary modules;loading> modules. Normally we recommend that you do this at server startup via a PerlModule directive> PerlModule directive in your httpd.conf file or in your handler.pl file to save memory, but we load them here just to show you which ones we are using. The component uses the Apache::Cookie module to fetch any cookies that might have been sent by the browser. Then we check for the existence of a cookie called masonbook_session, which if it exists should contain a valid session ID.

  local *MasonBook::Session;
  
  eval {
      tie %MasonBook::Session, 'Apache::Session::File', $session_id, {
          Directory => '/tmp/sessions',
          LockDirectory => '/tmp/sessions',
      };
  };
  
  if ($@) {
      die $@ unless $@ =~ /Object does not exist/;  # Re-throw
  
      $m->redirect('/bad_session.html');       
  }

The first line ensures that when this component ends, the session variable will go out of scope, which triggers Apache::Session's cleanup mechanisms. This is quite important, as otherwise the data will never be written to disk. Even worse, Apache::Session may still be maintaining various locks internally, leading to deadlock. We use local( ) local( ) method>

to localize the symbol table entry *MasonBook::Session; it's not enough to localize just the hash %MasonBook::Session, because the tie( ) tie( ) method>

magic is attached to the symbol table entry. It's also worth mentioning that we use a global variable rather than a lexical one, because we want this variable to be available to all components.

If the value in the $session_id variable is undef, that is not a problem. The Apache::Session__FOX_NLBF__> __FOX_NLBF__> module simply creates a new session ID. However, if $session_id is defined but does not represent a valid session, an exception will be thrown. This means either that the user's session has expired or that she's trying to feed us a bogus ID. Either way, we want to tell her what's happened, so we redirect to another page that will explain things. To trap the exception, we wrap the tie( ) in an eval {} block.

If an exception is thrown, we check $@ to see whether the message indicates that the session isn't valid. Any other error is fatal. If the session isn't valid, we use the redirect( ) method provided by the request object.

Finally, we send the user a cookie:

  Apache::Cookie->new( $r,
                       name => 'masonbook_session',
                       value => $MasonBook::Session{_session_id},
                       path => '/',
                       expires => '+1d',
                     )->bake;

This simply uses the Apache::Cookie Apache::Cookie module> modules;Apache::Cookie> module to ensure that a cookie will be sent to the client with the response headers. This cookie is called 'masonbook_session' and is the one we checked for earlier. It doesn't hurt to send the cookie every time a page is viewed, though this will reset the expiration time of the cookie each time it is set. If you want the cookie to persist for only a certain fixed length of time after the session is created, don't resend the __FOX_NLBF__> __FOX_NLBF__> __FOX_NLBF__> cookie.

  $m->call_next;
  </%init>

This line simply calls the next component in the inheritance chain. Presumably, other components down the line may change the contents of %MasonBook::Session, and those modifications will be written to disk at the end of the request.

A<CHP-11-EX-1>Example 11-1 shows the entire component.