cvsuser 02/02/15 10:38:38
Modified: P5EEx/Blue/P5EEx/Blue Context.pm Session.pm
P5EEx/Blue/P5EEx/Blue/Context HTML.pm
P5EEx/Blue/P5EEx/Blue/Session HTMLHidden.pm
Added: P5EEx/Blue/P5EEx/Blue/Session Cookie.pm
Log:
Added cookie-based session maintenance
Revision Changes Path
1.14 +5 -1 p5ee/P5EEx/Blue/P5EEx/Blue/Context.pm
Index: Context.pm
===================================================================
RCS file: /cvs/public/p5ee/P5EEx/Blue/P5EEx/Blue/Context.pm,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -w -r1.13 -r1.14
--- Context.pm 8 Feb 2002 20:46:04 -0000 1.13
+++ Context.pm 15 Feb 2002 18:38:37 -0000 1.14
@@ -1,6 +1,6 @@
#############################################################################
-## $Id: Context.pm,v 1.13 2002/02/08 20:46:04 spadkins Exp $
+## $Id: Context.pm,v 1.14 2002/02/15 18:38:37 spadkins Exp $
#############################################################################
package P5EEx::Blue::Context;
@@ -209,6 +209,10 @@
$self->{config} = P5EEx::Blue::P5EE->new($config_class, "new", \%args);
$self->init(\%args);
+
+ $self->dbgprint("Context->new(): configClass=$config_class
sessionClass=$session_class")
+ if ($P5EEx::Blue::Context::DEBUG && $self->dbg(ref($self),"new",1));
+
$self->{session} = P5EEx::Blue::P5EE->new($session_class, "new", \%args);
$self->{initconfig} = \%args;
1.6 +5 -3 p5ee/P5EEx/Blue/P5EEx/Blue/Session.pm
Index: Session.pm
===================================================================
RCS file: /cvs/public/p5ee/P5EEx/Blue/P5EEx/Blue/Session.pm,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -w -r1.5 -r1.6
--- Session.pm 5 Feb 2002 22:29:12 -0000 1.5
+++ Session.pm 15 Feb 2002 18:38:37 -0000 1.6
@@ -1,6 +1,6 @@
#############################################################################
-## $Id: Session.pm,v 1.5 2002/02/05 22:29:12 spadkins Exp $
+## $Id: Session.pm,v 1.6 2002/02/15 18:38:37 spadkins Exp $
#############################################################################
package P5EEx::Blue::Session;
@@ -75,11 +75,13 @@
=item * Class: P5EEx::Blue::Session
+=item * Class: P5EEx::Blue::Session::HTMLHidden
+
+=item * Class: P5EEx::Blue::Session::Cookie
+
=item * Class: P5EEx::Blue::Session::ApacheSession
=item * Class: P5EEx::Blue::Session::ApacheSessionX
-
-=item * Class: P5EEx::Blue::Session::HTMLHidden
=back
1.5 +14 -3 p5ee/P5EEx/Blue/P5EEx/Blue/Context/HTML.pm
Index: HTML.pm
===================================================================
RCS file: /cvs/public/p5ee/P5EEx/Blue/P5EEx/Blue/Context/HTML.pm,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -w -r1.4 -r1.5
--- HTML.pm 8 Feb 2002 20:47:19 -0000 1.4
+++ HTML.pm 15 Feb 2002 18:38:38 -0000 1.5
@@ -1,6 +1,6 @@
#############################################################################
-## $Id: HTML.pm,v 1.4 2002/02/08 20:47:19 spadkins Exp $
+## $Id: HTML.pm,v 1.5 2002/02/15 18:38:38 spadkins Exp $
#############################################################################
package P5EEx::Blue::Context::HTML;
@@ -188,8 +188,9 @@
</html>
EOF
- if ($main::target) {
- $header .= "Window-target: $main::target\n";
+ if (defined $self->{headers}) {
+ $header .= $self->{headers};
+ delete $self->{headers}
}
if ($main::conf{gzip}) {
@@ -279,6 +280,16 @@
else {
$self->dbgprint(ref($self), "->set_head_html(): $key=[repeat]")
if ($P5EEx::Blue::Context::DEBUG >= 3 && $self->dbg(ref($self),
"set_head_html", 3));
+ }
+}
+
+sub set_header {
+ my ($self, $header) = @_;
+ if (defined $self->{headers}) {
+ $self->{headers} .= $header;
+ }
+ else {
+ $self->{headers} = $header;
}
}
1.4 +2 -1 p5ee/P5EEx/Blue/P5EEx/Blue/Session/HTMLHidden.pm
Index: HTMLHidden.pm
===================================================================
RCS file: /cvs/public/p5ee/P5EEx/Blue/P5EEx/Blue/Session/HTMLHidden.pm,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -w -r1.3 -r1.4
--- HTMLHidden.pm 5 Feb 2002 22:25:58 -0000 1.3
+++ HTMLHidden.pm 15 Feb 2002 18:38:38 -0000 1.4
@@ -1,6 +1,6 @@
#############################################################################
-## $Id: HTMLHidden.pm,v 1.3 2002/02/05 22:25:58 spadkins Exp $
+## $Id: HTMLHidden.pm,v 1.4 2002/02/15 18:38:38 spadkins Exp $
#############################################################################
package P5EEx::Blue::Session::HTMLHidden;
@@ -276,6 +276,7 @@
$state = thaw(Compress::Zlib::memGunzip(decode_base64($sessiontext)));
}
}
+ $self->{context} = $args->{context} if (defined $args->{context});
$self->{state} = $state;
$self->{cache} = {};
}
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/Session/Cookie.pm
Index: Cookie.pm
===================================================================
#############################################################################
## $Id: Cookie.pm,v 1.1 2002/02/15 18:38:38 spadkins Exp $
#############################################################################
package P5EEx::Blue::Session::Cookie;
use P5EEx::Blue::P5EE;
use P5EEx::Blue::Session;
@ISA = ( "P5EEx::Blue::Session" );
use strict;
use Data::Dumper;
use Storable qw(freeze thaw);
use Compress::Zlib;
use MIME::Base64;
# note: We may want to apply an HMAC (hashed message authentication code)
# so that users cannot fiddle with the values.
# We may also want to add IP address and timeout for security.
# We may also want to add encryption so they can't even decode the data.
# use Digest::HMAC_MD5;
# use Crypt::CBC;
=head1 NAME
P5EEx::Blue::Session::Cookie - a session whose state is maintained across
HTML requests by being embedded in an HTML <input type="hidden"> tag.
=head1 SYNOPSIS
# ... official way to get a Session object ...
use P5EEx::Blue::P5EE;
$session = P5EEx::Blue::P5EE->session();
$session = $session->session(); # get the session
# any of the following named parameters may be specified
$session = $session->session(
);
# ... alternative way (used internally) ...
use P5EEx::Blue::Session::Cookie;
$session = P5EEx::Blue::Session->new();
=cut
#############################################################################
# CONSTANTS
#############################################################################
=head1 DESCRIPTION
A Session class models the sequence of events associated with a
use of the system. These events may occur in different processes.
Yet the accumulated state of the session needs to be propagated from
one process to the next.
This Session::Cookie maintains its state across
HTML requests by being embedded in an HTTP cookie.
As a result, it requires no server-side storage, so the sessions
never need to time out.
The Session::Cookie has an advantage over Session::HTMLHidden in that
data does not need to be posted to a URL for the session data to be
transmitted to it. This allows that the state can be propagated
properly to sub-components of an HTML page such as
* frame documents within a frameset (<frame src=...>)
* dynamically generated images (<img src=...>, <input type=image src=...>)
Limits on cookie storage are as follows, according to "Dynamic HTML,
The Definitive Reference" by O'Reilly in the DOM Reference under
"document.cookie".
* max 2000 chars per cookie (recommended, although 4000 supposedly allowed)
* max 20 cookies per domain
This allows for roughly 40K of session storage.
It is quite conceivable that this amount of storage could be overrun,
so Session::Cookie is only appropriate in situations where you are confident
it will not be. Also, widgets should take care to clean up after themselves,
and static values stored in the session can alternatively be provided in
the config.
=cut
#############################################################################
# CONSTRUCTOR METHODS
#############################################################################
=head1 Constructor Methods:
=cut
#############################################################################
# new()
#############################################################################
=head2 new()
The constructor is inherited from
L<C<P5EEx::Blue::Service>|P5EEx::Blue::Service/"new()">.
=cut
#############################################################################
# PUBLIC METHODS
#############################################################################
=head1 Public Methods:
=cut
#############################################################################
# get_session_id()
#############################################################################
=head2 get_session_id()
* Signature: $session_id = $session->get_session_id();
* Param: void
* Return: $session_id string
* Throws: <none>
* Since: 0.01
Sample Usage:
$session->get_session_id();
The get_session_id() returns the session_id of this particular session.
This session_id is unique for all time. If a session_id does not yet
exist, one will be created. The session_id is only created when first
requested, and not when the session is instantiated.
=cut
sub get_session_id {
my $self = shift;
"cookie";
}
#############################################################################
# html()
#############################################################################
=head2 html()
The html() method ...
* Signature: $html = $session->html();
* Param: void
* Return: $html string
* Throws: <none>
* Since: 0.01
Sample Usage:
$session->html();
This method returns the empty string ("") as the HTML to be embedded in
the page. This is because a session_id does not need to be stored.
However, it has the side effect that cookies are prepared for the HTTP
response headers.
=cut
sub html {
my ($self, $options) = @_;
my ($sessiontext, $sessiondata, $html, $headers, $cookieoptions);
$sessiondata = $self->{state};
$sessiontext = encode_base64(Compress::Zlib::memGzip(freeze($sessiondata)));
my ($maxvarsize, $maxvarlines);
# length of a MIME/Base64 line is (76 chars + newline)
# the max length of a cookie should be 2000 chars
$maxvarlines = 25;
$maxvarsize = $maxvarlines*77; # 1925 chars
$headers = "";
$cookieoptions = ""; # TODO: expires, path, domain, secure
$html = "";
if (length($sessiontext) <= $maxvarsize) {
$sessiontext =~ s/\n//g; # get rid of newlines (76 char lines)
$headers = "Set-Cookie: p5ee_sessiondata=$sessiontext$cookieoptions\n";
$self->{context}->set_header($headers);
}
else {
my (@sessiontext, $i, $startidx, $endidx, $textchunk);
@sessiontext = split(/\n/,$sessiontext);
$i = 1;
$startidx = 0;
$endidx = $startidx+$maxvarlines-1;
$textchunk = join("",@sessiontext[$startidx .. $endidx]);
$headers .= "Set-Cookie: p5ee_sessiondata=$textchunk$cookieoptions\n";
while ($endidx < $#sessiontext) {
$i++;
$startidx += $maxvarlines;
$endidx = $startidx+$maxvarlines-1;
$endidx = $#sessiontext if ($endidx > $#sessiontext-1);
$textchunk = join("",@sessiontext[$startidx .. $endidx]);
$headers .= "Set-Cookie:
p5ee_sessiondata${i}=$textchunk$cookieoptions\n";
}
$self->{context}->set_header($headers);
}
if ($options && $options->{showsession}) {
# DEBUGGING ONLY
my $d = Data::Dumper->new([ $sessiondata ], [ "sessiondata" ]);
$d->Indent(1);
$html .= "<!-- Contents of the session. (For debugging only. Should be
turned off in production.)\n";
$html .= $d->Dump();
$html .= "-->\n";
}
$html;
}
#############################################################################
# PROTECTED METHODS
#############################################################################
=head1 Protected Methods:
The following methods are intended to be called by subclasses of the
current class.
=cut
#############################################################################
# create()
#############################################################################
=head2 create()
The create() method is used to create the Perl structure that will
be blessed into the class and returned by the constructor.
* Signature: $ref = P5EEx::Blue::Reference->create($hashref)
* Param: $hashref {}
* Return: $ref ref
* Throws: P5EEx::Blue::Exception
* Since: 0.01
Sample Usage:
=cut
sub create {
my ($self, $args) = @_;
$args = {} if (!defined $args);
my ($ref);
$ref = {};
$ref;
}
#############################################################################
# init()
#############################################################################
=head2 init()
The init() method is called from within the constructor.
* Signature: init($named)
* Param: $named {} [in]
* Return: void
* Throws: P5EEx::Blue::Exception
* Since: 0.01
Sample Usage:
$ref->init($args);
The init() method looks at the cookies in the request
and restores the session state information from the cookies
named "p5ee_sessiondata" (and "p5ee_sessiondata[2..n]").
When the values of these cookies are concatenated, they
form a Base64-encoded, gzipped, frozen multi-level hash of
session state data. To retrieve the state data, the text
is therefore decoded, gunzipped, and thawed (a la Storable).
TODO: encrypt and MAC
=cut
sub init {
my ($self, $args) = @_;
my ($cgi, $sessiontext, $state);
$cgi = $args->{cgi} if (defined $args);
$state = {};
if (defined $cgi) {
$sessiontext = $cgi->cookie("p5ee_sessiondata");
if ($sessiontext) {
my ($i, $textchunk);
$i = 2;
while (1) {
$textchunk = $cgi->cookie("p5ee_sessiondata${i}");
last if (!$textchunk);
$sessiontext .= $textchunk;
$i++;
}
$sessiontext =~ s/(.{76})/$1\n/g;
$sessiontext .= "\n";
$state = thaw(Compress::Zlib::memGunzip(decode_base64($sessiontext)));
}
}
$self->{context} = $args->{context} if (defined $args->{context});
$self->{state} = $state;
$self->{cache} = {};
}
1;