Apache-ASP
view release on metacpan or search on metacpan
# set up globals as early as Application_OnStart, also
# allows variables to be changed in Script_OnStart for running script
&InitPackageGlobals($self);
if(my $ref = ref $code) {
if($ref eq 'CODE') {
eval { &$code(); };
} elsif($ref eq 'SCALAR') {
# $self->{dbg} && $self->Debug("writing cached static file data $code, length: ".length($$code));
$self->{Response}->WriteRef($code);
} else {
$self->Error("$code is a ref, but not CODE or SCALAR!");
}
} else {
# if absolute package already, then no need to set to package namespace
my $subid = ( $code =~ /::/ ) ? $code : $self->{GlobalASA}{'package'}.'::'.$code;
eval { &$subid(); };
}
if($@) {
$self->Error($@);
}
! $@;
}
sub Cache {
my($self, $cache_name, $key, $value, $expires, $last_modified, $no_check_meta) = @_;
$cache_name || die("no cache_name given");
grep($cache_name eq $_, qw(XSLT Response)) || die("cache_name $cache_name is invalid");
return unless defined($key);
my $cache_dbm = $self->{Caches}{$cache_name};
if(defined $cache_dbm) {
$self->{dbg} && $self->Debug("found cache $cache_dbm for $cache_name");
} else {
# load at runtime for CGI environments, preloaded for mod_perl
require Apache::ASP::State;
local $self->{state_dir} = &config($self, 'CacheDir') || $self->{state_dir};
local $self->{state_db} = &config($self, 'CacheDB') || 'MLDBM::Sync::SDBM_File';
$self->{dbg} && $self->Debug("CacheDB set to $self->{state_db}");
$cache_dbm = Apache::ASP::State::new($self, $cache_name, 'cache')
|| ($self->Error("could not do cache $cache_name: $!") && return);
$self->{Caches}{$cache_name} = $cache_dbm;
$self->{dbg} && $self->Debug("init cache $cache_dbm for $cache_name");
}
$key = (ref($key) && ($key =~ /SCALAR/)) ? $$key : $key;
my $checksum = &md5_hex($key).'x'.length($key);
my $metakey = $checksum . 'xMETA';
my $rv;
eval {
$cache_dbm->{dbm}->Lock;
if(defined $value) {
my $meta = { ServerID => $ServerID, Creation => time() };
if(defined $expires && ($expires =~ /^\-?\d+$/)) {
$meta->{Expires} = $expires;
$meta->{Timeout} = time + $expires;
};
$self->{dbg} && $self->Debug("storing $checksum in $cache_name cache");
$cache_dbm->STORE($metakey, $meta);
$self->{cache_count_store}++;
$rv = $cache_dbm->STORE($checksum, $value);
} else {
# don't check meta data for XSLT since transformations don't expire ever
if($no_check_meta) {
$self->{dbg} && $self->Debug("cache $cache_name fetch checksum $checksum no check meta");
$self->{cache_count_fetch}++;
$rv = $cache_dbm->{dbm}->FETCH($checksum);
} else {
my $meta = $cache_dbm->{dbm}->FETCH($metakey);
my $new;
if(! $meta) {
$meta = { Creation => 0, ServerID => 'NULL' };
$new = 1;
} else {
# NEW EXPIRES FOR EXISTING ITEM
if(defined $expires && ($expires =~ /^\-?\d+$/) && ($expires != $meta->{Expires})) {
$self->Debug("new expires $expires, old ".($meta->{Expires} || '')." for $checksum");
$meta->{Expires} = $expires;
# use creation timestamp for expires calculation, not current
# time, or we would refresh the entry
$meta->{Timeout} = $meta->{Creation} + $expires;
$cache_dbm->STORE($metakey, $meta);
};
}
# LastModified calculations
if(defined $last_modified) {
if($last_modified !~ /^\d+$/) {
my $old_last_modified = $last_modified;
$last_modified = &Apache::ASP::Date::str2time($last_modified);
$self->{dbg} && $self->Debug("converting string date for LastModified $old_last_modified to unix time $last_modified");
}
if($last_modified < 0) {
$self->{dbg} && $self->Debug("negative LastModified $last_modified ignored");
$last_modified = undef;
}
}
# EARLY TIMEOUT CALCULATION
if($meta->{Timeout}) {
# 10% chance to expire early to prevent collision
my $early = ($meta->{Expires} || 0) * rand() * '.1';
$self->{dbg} && $self->Debug("will reduce expires for $meta->{Expires} by random $early seconds, checksum $checksum");
$meta->{Timeout} = $meta->{Timeout} - $early;
}
$self->{dbg} && $self->Debug("meta cache data for checksum $checksum", $meta);
if($new) {
$self->{dbg} && $self->Debug("no cache entry, checksum $checksum");
$self->{cache_count_miss}++;
$rv = undef;
} elsif(defined $meta->{ServerID} && ($$ ne $ServerPID) && ($meta->{ServerID} ne $ServerID)) {
# can only run like this when running in preloaded mod_perl mode
# This will allow for caching in other modes that simply does not reset
# upon server restart
$self->{dbg} && $self->Debug("cache expires new server $ServerID, was $meta->{ServerID}");
$self->{cache_count_restart}++;
$rv = undef;
} elsif($meta->{Timeout} && ($meta->{Timeout} <= time())) {
$self->{dbg} && $self->Debug("cache expires timeout $meta->{Timeout}, checksum $checksum, time ".time);
$self->{cache_count_expires}++;
$rv = undef;
} elsif(defined($last_modified) && ($last_modified >= $meta->{Creation})) {
$self->{dbg} && $self->Debug("cache expires, checksum $checksum, LastModified $last_modified, Creation $meta->{Creation}");
$self->{cache_count_last_modified_expires}++;
$rv = undef;
} else {
$self->{dbg} && $self->Debug("cache $cache_name fetch checksum $checksum");
$self->{cache_count_fetch}++;
$rv = $cache_dbm->{dbm}->FETCH($checksum);
}
}
}
$cache_dbm->{dbm}->UnLock;
};
if($@) {
$self->Out("[ASP WARN] error using cache $cache_name: $@");
$self->{cache_count_error}++;
eval { $cache_dbm->{dbm}->UnLock; };
}
$rv;
}
sub XSLT {
my($self, $xsl_data, $xml_data) = @_;
my $asp = $self;
my $cache = &config($self, 'XSLTCache');
my $cache_data = $$xsl_data.$$xml_data;
if($cache) {
if(my $data = $self->Cache('XSLT', \$cache_data, undef, undef, undef, 1)) {
return $data;
}
}
ref($xsl_data) || die("xsl data must be a scalar ref");
my $xslt_parser = &config($self, 'XSLTParser') || 'XML::XSLT';
my @parsers = ('XML::XSLT 0.32', 'XML::Sablotron', 'XML::LibXSLT');
my $xslt_parser_lib;
unless (($xslt_parser_lib) = grep(/^$xslt_parser/, @parsers)) {
die("$xslt_parser must be one of: ".join(',', @parsers));
}
$asp->{dbg} && $asp->Debug("using xslt parser $xslt_parser_lib");
eval "use $xslt_parser_lib";
$@ && die("failed to load $xslt_parser_lib: $@");
my $xslt_data = '';
return \$xslt_data unless(length($$xsl_data) && length($$xml_data));
if ($xslt_parser eq 'XML::XSLT') {
my $xslt = XML::XSLT->new($xsl_data);
$xslt->transform($xml_data);
$xslt_data = $xslt->toString;
$xslt->dispose;
} elsif ($xslt_parser eq 'XML::Sablotron') {
$html =~s/</</gs;
$html;
}
# quickly decomped out of Apache::ASP just to optionally load
# it at runtime for CGI programs ( which shouldn't need it anyway )
# will still precompile this for mod_perl
#
sub StatINC {
my $self = shift;
require Apache::ASP::StatINC;
$self->StatINCRun;
}
sub SendMail {
my($self, $mail, %args) = @_;
my($smtp, @to, $server);
my $rv = 1;
# load option mail modules
for('Net::Config', 'Net::SMTP') {
eval "use $_";
if($@) {
die("no mailing errors because can't load $_: $@");
return 0;
}
}
# configure mail host
if($self->{mail_host} = &config($self, 'MailHost')) {
unless($NetConfig{smtp_hosts} && (($NetConfig{smtp_hosts}->[0] || '') eq $self->{mail_host})) {
unshift(@{$NetConfig{smtp_hosts}}, $self->{mail_host});
}
}
$mail->{From} ||= &config($self, 'MailFrom');
unless($mail->{Test}) {
for('To', 'Body', 'Subject', 'From') {
$mail->{$_} ||
die("need $_ argument to send mail");
}
}
# debugging set in mail args, or general debugging
if(! defined($args{Debug}) && defined($mail->{Debug})) {
$args{Debug} = $mail->{Debug};
delete $mail->{Debug};
}
if(! defined($args{Debug})) {
# in case of system level debugging, mark Net::SMTP debug also
if((&config($self, 'Debug') || 0) < 0) {
$args{Debug} = 1;
}
}
# connect to server
{
local $SIG{__WARN__} = sub { $self->Debug('Net::SMTP->new() warning', @_) };
if($mail->{Test}) {
$args{Timeout} = 5;
}
$smtp = Net::SMTP->new(%args);
}
unless($smtp) {
$self->Out("[ERROR] can't connect to SMTP server with args ", \%args);
return 0;
} else {
$self->Debug("connected to SMTP server with args ", \%args);
}
for my $receivers (qw(To BCC CC)) {
next unless $mail->{$receivers};
my @receivers = (ref $mail->{$receivers}) ? @{$mail->{$receivers}} : (split(/\s*,\s*/, $mail->{$receivers}));
push(@to, @receivers);
}
$self->Debug("sending mail to: ".join(',', @to));
($mail->{From}) = split(/\s*,\s*/,($mail->{From} || '')); # just the first one
$smtp->mail($mail->{From}) || return(0);
# put test before $smtp->to() because we might get a relaying denied error otherwise
if($mail->{Test}) {
return $rv;
}
$smtp->to(@to) || return(0);
my($data);
my $body = $mail->{Body};
delete $mail->{Body};
# assumes MIME-Version 1.0 for Content-Type header, according to RFC 1521
# http://www.ietf.org/rfc/rfc1521.txt
if($mail->{'Content-Type'} && ! $mail->{'MIME-Version'}) {
$mail->{'MIME-Version'} = '1.0';
}
my %done;
for('Subject', 'From', 'Reply-To', 'Organization', 'To', keys %$mail) {
next unless $mail->{$_};
next if $done{lc($_)}++;
my $add = ref($mail->{$_}) ? join(",", @{$mail->{$_}}) : $mail->{$_};
$add =~ s/^[\n]*(.*?)[\n]*$/$1/;
$data .= "$_: $add\n";
}
$data .= "\n" . $body;
$smtp->data($data) || ($rv = 0);
$smtp->quit();
$rv && $self->Debug("mail sent successfully");
$rv;
}
*LoadModule = *LoadModules;
sub LoadModules {
my($self, $category, @modules) = @_;
my $load_errors = 0;
include compilations are cached by the server. Using this configuration
will save on memory but will slow down script execution. Please
see the TUNING section for other strategies on improving site performance.
PerlSetVar NoCache 0
=head2 State Management
=item NoState
default 0, if true, neither the $Application nor $Session objects will
be created. Use this for a performance increase. Please note that
this setting takes precedence over the AllowSessionState and
AllowApplicationState settings.
PerlSetVar NoState 0
=item AllowSessionState
Set to 0 for no session tracking, 1 by default
If Session tracking is turned off, performance improves,
but the $Session object is inaccessible.
PerlSetVar AllowSessionState 1
Note that if you want to dissallow session creation
for certain non web browser user agents, like search engine
spiders, you can use an init handler like:
PerlInitHandler "sub { $_[0]->dir_config('AllowSessionState', 0) }"
=item AllowApplicationState
Default 1. If you want to leave $Application undefined, then set this
to 0, for a performance increase of around 2-3%. Allowing use of
$Application is less expensive than $Session, as there is more
work for the StateManager associated with $Session garbage collection
so this parameter should be only used for extreme tuning.
PerlSetVar AllowApplicationState 1
=item StateDir
default $Global/.state. State files for ASP application go to
this directory. Where the state files go is the most important
determinant in what makes a unique ASP application. Different
configs pointing to the same StateDir are part of the same
ASP application.
The default has not changed since implementing this config directive.
The reason for this config option is to allow operating systems with caching
file systems like Solaris to specify a state directory separately
from the Global directory, which contains more permanent files.
This way one may point StateDir to /tmp/myaspapp, and make one's ASP
application scream with speed.
PerlSetVar StateDir ./.state
=item StateManager
default 10, this number specifies the numbers of times per SessionTimeout
that timed out sessions are garbage collected. The bigger the number,
the slower your system, but the more precise Session_OnEnd's will be
run from global.asa, which occur when a timed out session is cleaned up,
and the better able to withstand Session guessing hacking attempts.
The lower the number, the faster a normal system will run.
The defaults of 20 minutes for SessionTimeout and 10 times for
StateManager, has dead Sessions being cleaned up every 2 minutes.
PerlSetVar StateManager 10
=item StateDB
default SDBM_File, this is the internal database used for state
objects like $Application and $Session. Because an SDBM_File %hash
has a limit on the size of a record key+value pair, usually 1024 bytes,
you may want to use another tied database like DB_File or
MLDBM::Sync::SDBM_File.
With lightweight $Session and $Application use, you can get
away with SDBM_File, but if you load it up with complex data like
$Session{key} = { # very large complex object }
you might max out the 1024 limit.
Currently StateDB can be: SDBM_File, MLDBM::Sync::SDBM_File,
DB_File, and GDBM_File. Please let me know if you would like to
add any more to this list.
As of version .18, you may change this setting in a live production
environment, and new state databases created will be of this format.
With a prior version if you switch to a new StateDB, you would want to
delete the old StateDir, as there will likely be incompatibilities between
the different database formats, including the way garbage collection
is handled.
PerlSetVar StateDB SDBM_File
=item StateCache
Deprecated as of 2.23. There is no equivalent config for
the functionality this represented from that version on.
The 2.23 release represented a significant rewrite
of the state management, moving to MLDBM::Sync for its
subsystem.
=item StateSerializer
default Data::Dumper, you may set this to Storable for
faster serialization and storage of data into state objects.
This is particularly useful when storing large objects in
$Session and $Application, as the Storable.pm module has a faster
implementation of freezing and thawing data from and to
perl structures. Note that if you are storing this much
data in your state databases, you may want to use
DB_File since it does not have the default 1024 byte limit
that SDBM_File has on key/value lengths.
This configuration setting may be changed in production
as the state database's serializer type is stored
in the internal state manager which will always use
Data::Dumper & SDBM_File to store data.
PerlSetVar StateSerializer Data::Dumper
=head2 Sessions
=item CookiePath
URL root that client responds to by sending the session cookie.
If your asp application falls under the server url "/asp",
then you would set this variable to /asp. This then allows
you to run different applications on the same server, with
different user sessions for each application.
PerlSetVar CookiePath /
=item CookieDomain
Default 0, this NON-PORTABLE configuration will allow sessions to span
multiple web sites that match the same domain root. This is useful if
your web sites are hosted on the same machine and can share the same
StateDir configuration, and you want to shared the $Session data
across web sites. Whatever this is set to, that will add a
; domain=$CookieDomain
part to the Set-Cookie: header set for the session-id cookie.
PerlSetVar CookieDomain .your.global.domain
=item SessionTimeout
Default 20 minutes, when a user's session has been inactive for this
period of time, the Session_OnEnd event is run, if defined, for
that session, and the contents of that session are destroyed.
PerlSetVar SessionTimeout 20
=item SecureSession
default 0. Sets the secure tag for the session cookie, so that the cookie
will only be transmitted by the browser under https transmissions.
PerlSetVar SecureSession 1
=item HTTPOnlySession
default 0. Sets HttpOnly flag to session cookie to mitigate XSS attacks.
Supported by most modern browsers, it only allows access to the
session cookie by the server (ie NOT Javascript)
PerlSetVar HTTPOnlySession 1
=item ParanoidSession
default 0. When true, stores the user-agent header of the browser
that creates the session and validates this against the session cookie presented.
If this check fails, the session is killed, with the rationale that
there is a hacking attempt underway.
This config option was implemented to be a smooth upgrade, as
you can turn it off and on, without disrupting current sessions.
Sessions must be created with this turned on for the security to take effect.
This config option is to help prevent a brute force cookie search from
being successful. The number of possible cookies is huge, 2^128, thus making such
a hacking attempt VERY unlikely. However, on the off chance that such
an attack is successful, the hacker must also present identical
browser headers to authenticate the session, or the session will be
destroyed. Thus the User-Agent acts as a backup to the real session id.
The IP address of the browser cannot be used, since because of proxies,
IP addresses may change between requests during a session.
There are a few browsers that will not present a User-Agent header.
These browsers are considered to be browsers of type "Unknown", and
this method works the same way for them.
Most people agree that this level of security is unnecessary, thus
it is titled paranoid :)
PerlSetVar ParanoidSession 0
=item SessionSerialize
default 0, if true, locks $Session for duration of script, which
serializes requests to the $Session object. Only one script at
a time may run, per user $Session, with sessions allowed.
Serialized requests to the session object is the Microsoft ASP way,
but is dangerous in a production environment, where there is risk
of long-running or run-away processes. If these things happen,
a session may be locked for an indefinite period of time. A user
STOP button should safely quit the session however.
PerlSetVar SessionSerialize 0
=item SessionCount
* These are API extensions that are not portable, but were
added because they are incredibly useful
These actions must be defined in the $Global/global.asa file
as subroutines, for example:
sub Session_OnStart {
$Application->{$Session->SessionID()} = started;
}
Sessions are easy to understand. When visiting a page in a
web application, each user has one unique $Session. This
session expires, after which the user will have a new
$Session upon revisiting.
A web application starts when the user visits a page in that
application, and has a new $Session created. Right before
the first $Session is created, the $Application is created.
When the last user $Session expires, that $Application
expires also. For some web applications that are always busy,
the Application_OnEnd event may never occur.
=head2 Script_OnStart & Script_OnEnd
The script events are used to run any code for all scripts
in an application defined by a global.asa. Often, you would
like to run the same code for every script, which you would
otherwise have to add by hand, or add with a file include,
but with these events, just add your code to the global.asa,
and it will be run.
There is one caveat. Code in Script_OnEnd is not guaranteed
to be run when $Response->End() is called, since the program
execution ends immediately at this event. To always run critical
code, use the API extension:
$Server->RegisterCleanup()
=head2 Session_OnStart
Triggered by the beginning of a user's session, Session_OnStart
gets run before the user's executing script, and if the same
session recently timed out, after the session's triggered Session_OnEnd.
The Session_OnStart is particularly useful for caching database data,
and avoids having the caching handled by clumsy code inserted into
each script being executed.
=head2 Session_OnEnd
Triggered by a user session ending, Session_OnEnd can be useful
for cleaning up and analyzing user data accumulated during a session.
Sessions end when the session timeout expires, and the StateManager
performs session cleanup. The timing of the Session_OnEnd does not
occur immediately after the session times out, but when the first
script runs after the session expires, and the StateManager allows
for that session to be cleaned up.
So on a busy site with default SessionTimeout (20 minutes) and
StateManager (10 times) settings, the Session_OnEnd for a particular
session should be run near 22 minutes past the last activity that Session saw.
A site infrequently visited will only have the Session_OnEnd run
when a subsequent visit occurs, and theoretically the last session
of an application ever run will never have its Session_OnEnd run.
Thus I would not put anything mission-critical in the Session_OnEnd,
just stuff that would be nice to run whenever it gets run.
=head2 Script_OnFlush
API extension. This event will be called prior to flushing
the $Response buffer to the web client. At this time,
the $Response->{BinaryRef} buffer reference may be used to modify
the buffered output at runtime to apply global changes to scripts
output without having to modify all the scripts.
sub Script_OnFlush {
my $ref = $Response->{BinaryRef};
$$ref =~ s/\s+/ /sg; # to strip extra white space
}
Check out the ./site/eg/global.asa for an example of its use.
=head2 Script_OnParse
This event allows one to set up a source filter on the script text,
allowing one to change the script on the fly before the compilation
stage occurs. The script text is available in the $Server->{ScriptRef}
scalar reference, and can be accessed like so:
sub Script_OnParse {
my $code = $Server->{ScriptRef}
$$code .= " ADDED SOMETHING ";
}
=head2 Application_OnStart
This event marks the beginning of an ASP application, and
is run just before the Session_OnStart of the first Session
of an application. This event is useful to load up
$Application with data that will be used in all user sessions.
=head2 Application_OnEnd
The end of the application is marked by this event, which
is run after the last user session has timed out for a
given ASP application.
=head2 Server_OnStart ( pseudo-event )
Some might want something like a Server_OnStart event, where
some code gets runs when the web server starts. In mod_perl,
this is easy to achieve outside of the scope of an ASP
application, by putting some initialization code into
a <Perl> section in the httpd.conf file. Initializations
that you would like to be shared with the child httpds are
particularly useful, one such being the Apache::ASP->Loader()
routine which you can read more about in the TUNING section -
Precompile Scripts subsection. It is could be called like:
$Site->DESTROY;
$Site = $Form = undef;
});
}
In this way you can create site wide application objects
and simple aliases for common functions.
=head2 $Session Object
The $Session object keeps track of user and web client state, in
a persistent manner, making it relatively easy to develop web
applications. The $Session state is stored across HTTP connections,
in database files in the Global or StateDir directories, and will
persist across web server restarts.
The user session is referenced by a 128 bit / 32 byte MD5 hex hashed cookie,
and can be considered secure from session id guessing, or session hijacking.
When a hacker fails to guess a session, the system times out for a
second, and with 2**128 (3.4e38) keys to guess, a hacker will not be
guessing an id any time soon.
If an incoming cookie matches a timed out or non-existent session,
a new session is created with the incoming id. If the id matches a
currently active session, the session is tied to it and returned.
This is also similar to the Microsoft ASP implementation.
The $Session reference is a hash ref, and can be used as such to
store data as in:
$Session->{count}++; # increment count by one
%{$Session} = (); # clear $Session data
The $Session object state is implemented through MLDBM,
and a user should be aware of the limitations of MLDBM.
Basically, you can read complex structures, but not write
them, directly:
$data = $Session->{complex}{data}; # Read ok.
$Session->{complex}{data} = $data; # Write NOT ok.
$Session->{complex} = {data => $data}; # Write ok, all at once.
Please see MLDBM for more information on this topic.
$Session can also be used for the following methods and properties:
=over
=item $Session->{CodePage}
Not implemented. May never be until someone needs it.
=item $Session->{LCID}
Not implemented. May never be until someone needs it.
=item $Session->{SessionID}
SessionID property, returns the id for the current session,
which is exchanged between the client and the server as a cookie.
=item $Session->{Timeout} [= $minutes]
Timeout property, if minutes is being assigned, sets this
default timeout for the user session, else returns
the current session timeout.
If a user session is inactive for the full
timeout, the session is destroyed by the system.
No one can access the session after it times out, and the system
garbage collects it eventually.
=item $Session->Abandon()
The abandon method times out the session immediately. All Session
data is cleared in the process, just as when any session times out.
=item $Session->Lock()
API extension. If you are about to use $Session for many consecutive
reads or writes, you can improve performance by explicitly locking
$Session, and then unlocking, like:
$Session->Lock();
$Session->{count}++;
$Session->{count}++;
$Session->{count}++;
$Session->UnLock();
This sequence causes $Session to be locked and unlocked only
1 time, instead of the 6 times that it would be locked otherwise,
2 for each increment with one to read and one to write.
Because of flushing issues with SDBM_File and DB_File databases,
each lock actually ties fresh to the database, so the performance
savings here can be considerable.
Note that if you have SessionSerialize set, $Session is
already locked for each script invocation automatically, as if
you had called $Session->Lock() in Script_OnStart. Thus you
do not need to worry about $Session locking for performance.
Please read the section on SessionSerialize for more info.
=item $Session->UnLock()
API Extension. Unlocks the $Session explicitly. If you do not call this,
$Session will be unlocked automatically at the end of the
script.
=back
=head2 $Response Object
This object manages the output from the ASP Application and the
client web browser. It does not store state information like the
$Session object but does have a wide array of methods to call.
=over
=item $Response->{BinaryRef}
API extension. This is a perl reference to the buffered output of
the $Response object, and can be used in the Script_OnFlush
global.asa event to modify the buffered output at runtime
The Lock and Unlock methods are used to prevent simultaneous
access to the $Application object.
=over
=item $Application->Lock()
Locks the Application object for the life of the script, or until
UnLock() unlocks it, whichever comes first. When $Application
is locked, this guarantees that data being read and written to it
will not suddenly change on you between the reads and the writes.
This and the $Session object both lock automatically upon
every read and every write to ensure data integrity. This
lock is useful for concurrent access control purposes.
Be careful to not be too liberal with this, as you can quickly
create application bottlenecks with its improper use.
=item $Application->UnLock()
Unlocks the $Application object. If already unlocked, does nothing.
=item $Application->GetSession($sess_id)
This NON-PORTABLE API extension returns a user $Session given
a session id. This allows one to easily write a session manager if
session ids are stored in $Application during Session_OnStart, with
full access to these sessions for administrative purposes.
Be careful not to expose full session ids over the net, as they
could be used by a hacker to impersonate another user. So when
creating a session manager, for example, you could create
some other id to reference the SessionID internally, which
would allow you to control the sessions. This kind of application
would best be served under a secure web server.
The ./site/eg/global_asa_demo.asp script makes use of this routine
to display all the data in current user sessions.
=item $Application->SessionCount()
This NON-PORTABLE method returns the current number of active sessions
in the application, and is enabled by the SessionCount configuration setting.
This method is not implemented as part of the original ASP
object model, but is implemented here because it is useful. In particular,
when accessing databases with license requirements, one can monitor usage
effectively through accessing this value.
=back
=head2 $Server Object
The server object is that object that handles everything the other
objects do not. The best part of the server object for Win32 users is
the CreateObject method which allows developers to create instances of
ActiveX components, like the ADO component.
=over
=item $Server->{ScriptTimeout} = $seconds
Not implemented. May never be. Please see the
Apache Timeout configuration option, normally in httpd.conf.
=item $Server->Config($setting)
API extension. Allows a developer to read the CONFIG
settings, like Global, GlobalPackage, StateDir, etc.
Currently implemented as a wrapper around
Apache->dir_config($setting)
May also be invoked as $Server->Config(), which will
return a hash ref of all the PerlSetVar settings.
=item $Server->CreateObject($program_id)
Allows use of ActiveX objects on Win32. This routine returns
a reference to an Win32::OLE object upon success, and nothing upon
failure. It is through this mechanism that a developer can
utilize ADO. The equivalent syntax in VBScript is
Set object = Server.CreateObject(program_id)
For further information, try 'perldoc Win32::OLE' from your
favorite command line.
=item $Server->Execute($file, @args)
New method from ASP 3.0, this does the same thing as
$Response->Include($file, @args)
and internally is just a wrapper for such. Seems like we
had this important functionality before the IIS/ASP camp!
=item $Server->File()
Returns the absolute file path to current executing script.
Same as Apache->request->filename when running under mod_perl.
ASP API extension.
=item $Server->GetLastError()
Not implemented, will likely not ever be because this is dependent
on how IIS handles errors and is not relevant in Apache.
=item $Server->HTMLEncode( $string || \$string )
Returns an HTML escapes version of $string. &, ", >, <, are each
escapes with their HTML equivalents. Strings encoded in this nature
should be raw text displayed to an end user, as HTML tags become
escaped with this method.
As of version 2.23, $Server->HTMLEncode() may take a string reference
for an optmization when encoding a large buffer as an API extension.
Here is how one might use one over the other:
my $buffer = '&' x 100000;
$buffer = $Server->HTMLEncode($buffer);
print $buffer;
- or -
-XMLSubsMatch will strip parens in a pattern match
so it does not interfere with internal matching use.
+XSLT integration allowing XML to be rendered by XSLT
on the fly. XSLT specifies XSL file to transform XML.
XSLTMatch is a regexp that matches XML file names, like \.xml$,
which will be transformed by XSLT setting, default .*
XSLTCacheSize when specified uses Tie::Cache to cached XML DOMs
internally and cache XSLT transformations output per XML/XSL
combination. XML DOM objects can take a lot of RAM, so use
this setting judiciously like setting to 100. Definitely
experiment with this value.
+More client info in the error mail feature, including
client IP, form data, query string, and HTTP_* client headers
+With Time::HiRes loaded, and Debug set to non 0,
will add a <!-- Apache::ASP served request in xx.xx seconds -->
to text/html output, similar to Cocoon, per user request
Will also add this to the system debug error log output
when Debug is < 0
-bug fix on object initialization optimization earlier
in this release, that was introduced for faster event
handler execution.
+Apache::ASP::Parse() takes a file name, scalar, or
scalar ref for arguments of data to parse for greater
integration ability with other applications.
+PodComments optimization, small speed increase at
compilation time.
+String optimization on internal rendering that avoids
unnecessary copying of static html, by using refs. Should
make a small difference on sites with large amounts of
static html.
+CompressGzip setting which, when Compress::Zlib is installed,
will compress text/html automatically going out to the web
browser if the client supports gzip encoding.
++Script_OnFlush event handler, and auxiliary work optimizing
asp events in general. $Response->{BinaryRef} created which
is a reference to outgoing output, which can be used
to modify the data at runtime before it goes out to the client.
+Some code optimizations that boost speed from 22 to 24
hits per second when using Sessions without $Application,
on a simple hello world benchmark on a WinNT PII300.
++Better SessionManagement, more aware of server farms that
don't have reliable NFS locking. The key here is to have only
one process on one server in charge of session garbage collection
at any one time, and try to create this situation with a snazzy
CleanupMaster routine. This is done by having a process register
itself in the internal database with a server key created at
apache start time. If this key gets stale, another process can
become the master, and this period will not exceed the period
SessionTimeout / StateManager.
** Work on session manager sponsored by LRN, http://www.lrn.com. **
** This work was used to deploy a server farm in production with **
** NFS mounted StateDir. Thanks to Craig Samuel for his belief in **
** open source. :) **
Future work for server farm capabilities might include breaking
up the internal database into one of 256 internal databases
hashed by the first 2 chars of the session id. Also on the plate
is Apache::Session like abilities with locking and/or data storage
occuring in a SQL database. The first dbs to be done will include
MySQL & Oracle.
+Better session security which will create a new session id for an
incoming session id that does not match one already seen. This will
help for those with Search engines that have bookmarked
pages with the session ids in the query strings. This breaks away
from standard ASP session id implementation which will automatically
use the session id presented by the browser, now a new session id will
be returned if the presented one is invalid or expired.
-$Application->GetSession will only return a session if
one already existed. It would create one before by default.
+Script_OnFlush global.asa event handler, and $Response->{BinaryRef}
member which is a scalar reference to the content about to be flushed.
See ./site/eg/global.asa for example usage, used in this case to
insert font tags on the fly into the output.
+Highlighting and linking of line error when Debug is set to 2 or -2.
--removed fork() call from flock() backup routine? How did
that get in there? Oh right, testing on Win32. :(
Very painful lesson this one, sorry to whom it may concern.
+$Application->SessionCount support turned off by default
must enable with SessionCount config option. This feature
puts an unnecessary load on busy sites, so not default
behavior now.
++XMLSubsMatch setting that allows the developer to
create custom tags XML style that execute perl subroutines.
See ./site/eg/xml_subs.asp
+MailFrom config option that defaults the From: field for
mails sent via the Mail* configs and $Server->Mail()
+$Server->Mail(\%mail, %smtp_args) API extension
+MailErrorsTo & MailAlertTo now can take comma
separated email addresses for multiple recipients.
-tracking of subroutines defined in scripts and includes so
StatINC won't undefine them when reloading the GlobalPackage,
and so an warning will be logged when another script redefines
the same subroutine name, which has been the bane of at least
a few developers.
-Loader() will now recompile dynamic includes that
have changed, even if main including script has not.
will throw off the line counts by adding text, removing
text, or having an extra newline added, respectively.
-Script_OnEnd may now send output to the browser. Before
$main::Response->End() was being called at the end of the
main script preventing further output.
++All scripts are compiled as routines in a namespace uniquely
defined by the global.asa of the ASP application. Thus,
scripts, includes, and global.asa routines will share
all globals defined in the global.asa namespace. This means
that globals between scripts will be shared, and globals
defined in a global.asa will be available to scripts.
Scripts used to have their own namespace, thus globals
were not shared between them.
+a -o $output_dir switch on the ./cgi/asp script allows
it to execute scripts and write their output to an output
directory. Useful for building static html sites, based on
asp scripts. An example use would be:
asp -b -o out *.asp
Without an output directory, script output is written to STDOUT
=item $VERSION = 0.09; $DATE="04/22/1999";
+Updated Makefile.PL optional modules output for CGI & DB_File
+Improved docs on $Response->Cookies() and $Request->Cookies()
+Added PERFORMANCE doc to main README, and added sub section
on precompiling scripts with Apache::ASP->Loader()
+Naming of CompileIncludes switched over to DynamicIncludes
for greater clarity.
+Dynamic includes can now reference ASP objects like $Session
w/o the $main::* syntax. These subs are no longer anonymous
subs, and are now compiled into the namespace of the global.asa package.
+Apache::ASP->Loader() precompiles dynamic includes too. Making this work
required fixing some subtle bugs / dependencies in the compiling process.
+Added Apache::ASP->Loader() similar to Apache::RegistryLoader for
precompiling ASP scripts. Precompile a whole site at server
startup with one function call.
+Prettied the error messaging with Debug 2.
+$Response->Debug(@args) debugging extension, which
allows a developer to hook into the module's debugging,
and only have @args be written to error_log when Debug is greater
than 0.
-Put write locking code around State writes, like $Session
and $Application. I thought I fixed this bug a while ago.
-API change: converted $Session->Timeout() and $Session->SessionID()
methods into $Session->{Timeout} and $Session->{SessionID} properties.
The use of these properties as methods is deprecated, but
backwards compatibility will remain. Updated ./eg/session.asp
to use these new properties.
+Implemented $Response->{PICS} which if set sends out a PICS-Label
HTTP header, useful for ratings.
+Implemented $Response->{CacheControl} and $Response->{Charset} members.
By default, CacheControl is 'private', and this value gets sent out
every request as HTTP header Cache-Control. Charset appends itself
onto the content type header.
+Implemented $Request->BinaryRead(), $Request->{TotalBytes},
documented them, and updated ./eg/form.asp for an example usage.
+Implemented $Response->BinaryWrite(), documented, and created
and example in ./eg/binary_write.htm
+Implemented $Server->MapPath() and created example of its use
in ./eg/server.htm
-$Request->Form() now reads file uploads correctly with
the latest CGI.pm, where $Request->Form('file_field') returns
the actual file name uploaded, which can be used as a file handle
to read in the data. Before, $Request->Form('file_field') would
return a glob that looks like *Fh::filename, so to get the file
name, you would have to parse it like =~ s/^\*Fh\:\://,
which you no longer have to do. As long as parsing was done as
mentioned, the change should be backwards compatible.
+Updated +enhanced documentation on file uploads. Created extra
comments about it as an FAQ, and under $Response->Form(), the latter
being an obvious place for a developer to look for it.
+Updated ./eg/file_upload.asp to show use of non file form data,
with which we had a bug before.
+Finished retieing *STDIN to cached STDIN contents, so that
CGI input routines may be used transparently, along side with
use of $Request->Form()
+Cleaned up and optimized $Request code
+Updated documentation for CGI input & file uploads. Created
file upload FAQ.
+Reworked ./eg/cgi.htm example to use CGI input routines
after doing a native read of STDIN.
++Added dynamic includes with <!--include file=file args=@args-->
extension. This style of include is compiled as an anonymous sub &
cached, and then executed with @args passed to the subroutine for
execution. This is include may also be rewritten as a new API
extension: $Response->Include('file', @args)
+Added ./eg/compiled_includes.htm example documenting new dynamic includes.
+Documented SSI: native file includes, and the rest with filtering
to Apache::SSI
code sharing
- <% @array... %> no longer dropped from code.
+perl =pod comments are stripped from script before compiling, and associated
PodComments configuration options.
+Command line cgi/asp script takes various options, and allows execution
of multiple asp scripts at one time. This script should be used for
command line debugging. This is also the beginning of building
a static site from asp scripts with the -b option, suppressing headers.
+$Response->AddHeader('Set-Cookie') works for multiple cookies.
-$Response->Cookies('foo', '0') works, was dropping 0 because of boolean test
-Fixed up some config doc errors.
=item $VERSION = 0.07; $DATE="01/20/1999";
-removed SIG{__WARN__} handler, it was a bad idea.
-fixes file locking on QNX, work around poor flock porting
+removed message about Win32::OLE on UNIX platforms from Makefile.PL
-Better lock garbage collection. Works with StatINC seamlessly.
-Multiple select forms now work in array context with $Response->Form()
@values = $Response->Form('multi');
-Better CGI.pm compatibility with $r->header_out('Content-type'),
improved garbage collection under modperl, esp. w/ file uploads
=item $VERSION = 0.06; $DATE="12/21/1998";
+Application_OnStart & Application_OnEnd event handlers support.
-Compatible with CGI.pm 2.46 headers()
-Compatible with CGI.pm $q = new CGI({}), caveat: does not set params
+use strict; followed by use of objects like $Session is fine.
-Multiple cookies may be set per script execution.
+file upload implemented via CGI.pm
++global.asa implemented with events Session_OnStart and Session_OnEnd
working appropriately.
+StateDir configuration directive implemented.
StateDir allows the session state directory to be specified separately
from the Global directory, useful for operating systems with caching file
systems.
+StateManager config directive. StateManager specifies how frequently
Sessions are cleaned up, with 10 (default) meaning that old Sessions
will be cleaned up 10 times per SessionTimeout period (default 20 minutes).
+$Application->SessionCount() implemented, non-portable method.
: returns the number of currently active sessions
-STOP button fix. Users may hit STOP button during script
execution, and Apache::ASP will cleanup with a routine registered
in Apache's $r->register_cleanup. Works well supposedly.
+PerlScript compatibility work, trying to make ports smoother.
: Collection emulator, no ->{Count} property
: $.*(.*)->{Item} parsed automatically,
shedding the ->{Item} for Collection support (? better way ?)
: No VBScript dates support, just HTTP RFC dates with HTTP::Date
: Win32::OLE::in not supported, just use "keys %{$Collection}"
+./cgi/asp script for testing scripts from the command line
: will be upgraded to CGI method of doing asp
: is not "correct" in anyway, so not documented for now
but still useful
+strips DOS carriage returns from scripts automatically, so that
programs like FrontPage can upload pages to UNIX servers
without perl choking on the extra \r characters.
=item $VERSION = 0.05; $DATE="10/19/1998";
+Added PERFORMANCE doc, which includes benchmarks +hints.
+Better installation warnings and errors for other modules required.
-Turned off StatINC in eg/.htaccess, as not everyone installs Devel::Symdump
-Fixed AUTOLOAD state bug, which wouldn't let you each through state
objects, like %{$Session}, or each %$Session, (bug introduced in v.04)
+Parses ASP white space better. HTML output matches author's intent
by better dealing with white space surrounding <% perl blocks %>
-Scalar insertion code <%=$foo%> can now span many lines.
+Added include.t test script for includes.
+Script recompiles when included files change.
+Files can be included in script with
SSI <!--#include file="filename"--> syntax, needs to be
done in ASP module to allow compilation of included code and html
into script. Future chaining with Apache::SSI will allow static
html includes, and other SSI directives
=item $VERSION = 0.04; $DATE="10/14/1998";
+Example script eg/cgi.htm demonstrating CGI.pm use for output.
+Optimized ASP parsing, faster and more legible executing code
: try 'die();' in code with setting PerlSetVar Debug 2
+Cleaned up code for running with 'use strict'
+$Response->Write() prints scalars, arrays, and hashes. Before only scalars.
+Begin implementation of $Server object.
+Implemented $Response->{Expires} and $Response->{ExpiresAbsolute}
+Added "PerlSetVar StatINC" config option
+$0 is aliased to current script filename
+ASP Objects ($Response, etc.) are set in main package
Thus notation like $main::Response->Write() can be used anywhere.
=item $VERSION = 0.02; $DATE="07/12/1998";
++Session Manager, won't break under denial of service attack
+Fleshed out $Response, $Session objects, almost full implementation.
+Enormously more documentation.
-Fixed error handling with Debug = 2.
-Documentation fixed for pod2man support. README now more man-like.
-Stripped \r\n dos characters from installation files
-755 mode set for session state directory when created
-Loads Win32/OLE properly, won't break with UNIX
=item $VERSION = 0.01; $DATE="06/26/1998";
Syntax Support
--------------
Initial release, could be considered alpha software.
Allows developers to embed perl in html ASP style.
<!-- sample here -->
<html>
<body>
<% for(1..10) { %>
counting: <%=$_%> <br>
<% } %>
</body>
</html>
ASP Objects
-----------
$Session, $Application, $Response, $Request objects available
for use in asp pages.
$Session & $Application data is preserved using SDBM files.
$Session id's are tracked through the use of cookies.
Security
--------
Timeouts any attempt to use a session id that doesn't already
exist. Should stop hackers, since there is no wire speed guessing
cookies.
=head1 LICENSE
Copyright (c) 1998-2018, Josh Chamas
All rights reserved. This program is free software; you can
redistribute it and/or modify it under the same terms as Perl itself.
Apache::ASP is a perl native port of Active Server Pages for Apache
and mod_perl.
=cut
( run in 1.356 second using v1.01-cache-2.11-cpan-56fb94df46f )