Apache-ASP

 view release on metacpan or  search on metacpan

ASP.pm  view on Meta::CPAN

    if($compressgzip_config) {
	if($self->LoadModule('Gzip','Compress::Zlib')) {
	    $self->{compressgzip} = 1;
	}
    }    
     
    # must have global directory into which we put the global.asa
    # and possibly state files, optimize out the case of . or ..
    if($self->{global} !~ /^(\.|\.\.)$/) {
	-d $self->{global} or 
	  $self->Error("global path, $self->{global}, is not a directory");
    }

    # includes_dir calculation
    if($filename =~ m,^((/|[a-zA-Z]:).*[/\\])[^/\\]+?$,) {
	$self->{dirname} = $1;
    } else {
	$self->{dirname} = '.';
    }
    $self->{includes_dir} = [
			     $self->{dirname},
			     $self->{global}, 
			     split(/;/, &config($self, 'IncludesDir') || ''),
			    ];

    # register cleanup before the state files get set in InitObjects
    # this way DESTROY gets called every time this script is done
    # we must cache $self for lookups later
    &RegisterCleanup($self, sub { $self->DESTROY });

    #### WAS INIT OBJECTS, REMOVED DECOMP FOR SPEED

    # GLOBALASA, RESPONSE, REQUEST, SERVER
    # always create these
    # global_asa assigns itself to parent object automatically
    my $global_asa = &Apache::ASP::GlobalASA::new($self);
    $self->{Request}   = &Apache::ASP::Request::new($self);
    $self->{Response}  = &Apache::ASP::Response::new($self);
    # Server::new() is just one line, so execute directly
    $self->{Server}    = bless {asp => $self}, 'Apache::ASP::Server';
    #&Apache::ASP::Server::new($self);

    # After GlobalASA Init, init the package that this script will execute in
    # must be here, and not end of new before things like Application_OnStart get run
    # UniquePackages & NoCache configs do not work together, NoCache wins here
    if(&config($self, 'UniquePackages')) {
	# id is not generally useful for the ASP object now, so calculate
	# it here now, only to twist the package object for this script

	# pass in basename for where to find the file for InodeNames, and the full path
	# for the FileId otherwise
	my $package = $global_asa->{'package'}.'::'.&FileId($self, $self->{basename}, $self->{filename});
	$self->{'package'} = $package;
	$self->{init_packages} = ['main', $global_asa->{'package'}, $self->{'package'}];	
    } else {
	$self->{'package'} = $global_asa->{'package'};
	$self->{init_packages} = ['main', $global_asa->{'package'}];	
    }

    $self->{state_dir}   = &config($self, 'StateDir', undef, $self->{global}.'/.state');
    $self->{state_dir}   =~ tr///; # untaint

    # if no state has been config'd, then set up none of the 
    # state objects: Application, Internal, Session
    unless(&get_dir_config($dir_config, 'NoState')) {
	# load at runtime for CGI environments, preloaded for mod_perl
	require Apache::ASP::StateManager;
	&InitState($self);
    }

    $self;
}

# called upon every end of connection by RegisterCleanup
sub DESTROY {
    my $self = shift;

    return unless $self->{destroy}; # still active object
    $self->{dbg} && $self->Debug("destroying ASP object $self");

    # do before undef'ing the object references in main
    for my $code ( @{$self->{cleanup}} ) {
	$self->{dbg} && $self->Debug("executing cleanup $code");
	eval { &$code() };
	$@ && $self->Error("executing cleanup $code error: $@");
    }

    local $^W = 0; # suppress untie while x inner references warnings
    select(STDOUT); 
    untie *RESPONSE if tied *RESPONSE;

    # can't move this to Request::DESTROY(), then CGI object compatibility
    # in test ./site/eg/cgi.htm test fails, don't know why, --jc, 12/06/2002
    untie *STDIN if tied *STDIN;

    # in case there is a dummy session here by the 
    # end of object execution
    if($self->{Session}) {
        if(eval { $self->{Session}->isa('Apache::ASP::Session') }) {
	    # only the cleanup master may cleanup groups now, so OK
	    # to call just CleanupGroups
	    $self->CleanupGroups();
	} else {
            $self->Debug("$self->{Session} is not an Apache::ASP::Session");
            eval { $self->{Session}->DESTROY };
            $self->{Session} = undef;
        }
    }

    # free file handles here.  mod_perl tends to be pretty clingy
    # to memory
    for('Application', 'Internal', 'Session') {
	# all this stuff in here is very necessary for total cleanup
	# the DESTROY is the most important, as we need to explicitly free
	# state objects, just in case anyone else is keeping references to them
	# But the destroy won't work without first untieing, go figure
	next unless defined $self->{$_};
	my $tied = tied %{$self->{$_}};
	next unless $tied;
	untie %{$self->{$_}};
	$tied->DESTROY(); # call explicit DESTROY
    }

    if(my $caches = $self->{Caches}) {
	# default cache size to 10M
	$self->{cache_size} = &config($self, 'CacheSize') || $CacheSize;
	if($self->{cache_size} =~ /^([\d\.]+)(M|K|B)?$/) {
	    my($size, $unit) = ($1, $2);
	    if($unit eq 'M') {
		$size *= 1024*1024;
	    } elsif($unit eq 'K') {
		$size *= 1024;
	    }
	    if($size ne $self->{cache_size}) {
		$self->{dbg} && $self->Debug("converting CacheSize $self->{cache_size} to $size bytes");
		$self->{cache_size} = $size;
	    }
	}
	for my $cache (values %$caches) {
	    my $tied = $cache;
	    if($tied->{writes} && $tied->Size > $self->{cache_size}) {
		$self->{dbg} && $self->Debug("deleting cache $cache, size: ".$tied->Size);
		$tied->Delete;
	    } else {
		$self->{dbg} && $self->Debug("cache $cache OK size, size: ".$tied->Size);
	    }
	    $tied->DESTROY();
	}
    }

    #    $self->{'dbg'} && $self->Debug("END ASP DESTROY");
    $self->{Request} && &Apache::ASP::Request::DESTROY($self->{Request});
    $self->{Server} && ( %{$self->{Server}} = () );
    $self->{Response} && ( %{$self->{Response}} = () );
    %$self = ();

    1;
}

sub RegisterCleanup {
    my $self = shift;

    if($ModPerl2) {
	$self->{r}->pool->cleanup_register(@_);
    } else {
	$self->{r}->register_cleanup(@_);
    }
}

sub InitPaths {

    # we load this module just to detect where the shared directory really is
    use Apache::ASP::Share::CORE;

    # major problem with %INC if we cannot get this information
    my $share_path = $INC{'Apache/ASP/Share/CORE.pm'} 
      || die(q(can't find path for $INC{'Apache/ASP/Share/CORE.pm'}));

    $share_path =~ s/CORE\.pm$//s;
    unless($share_path =~ /$AbsoluteFileMatch/) {
	# this %ENV manipulation is just to allow cwd() to run in taint check mode
	local %ENV = %ENV;
	$ENV{PATH} = '/bin:/usr/bin:/usr/sbin';
	delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
	my $currdir = cwd();
	$share_path = "$currdir/$share_path";
    }

    # not finding the ShareDir creates a hard error, because the Apache/ASP/Share
    # directory will become one of the fundamental underpinings of the project
    # People will need to rely on being able to load shared includes, and not have
    # to discover the lack of loading Share:: at runtime, rather this is a compile
    # time error.
    -d $share_path || die("Apache::ASP::Share directory not found.  ".
			  "Please make sure to install all the modules that make up the Apache::ASP installation."
			 );
    $ShareDir = $share_path;

    # once we find the $ShareDir, we can truncate the library path
    # and push it onto @INC with use lib... this is to help with loading
    # future Apache::ASP::* modules when the lib path it was found at is 
    # relative to some directory.  This was needed to have the "make test"
    # test suite to work which loads libraries from "blib/lib", but Apache::ASP
    # will chdir() into the script directory so that can ruin this
    # library lookup.
    #
    my $lib_path = $share_path;
    $lib_path =~ s/Apache.ASP.Share.?$//s;
    -d $lib_path || die("\%INC library path $lib_path not found.");
    $INCDir = $lib_path;
    
    # clear taint, for some reason, tr/// or s/^(.*)$/ did not work on perl 5.6.1
    $INCDir =~ /^(.*)$/s;
    $INCDir = $1;

    # make sure this gets on @INC at startup, can't hurt
    eval "use lib qw($INCDir);";

    1;
}

sub FileId {
    my($self, $file, $abs_file, $no_compile_checksum) = @_;
    $file || die("no file passed to FileId()");
    my $id;

    # calculate compile checksum for file id
    unless($self->{compile_checksum}) {
	my $r = $self->{r};
	my $checksum = md5_hex(join('&-+', 
				    $VERSION,
				    map { &config($self, $_) || '' }
				    @CompileChecksumKeys
				   )
			      );
	#    $self->{dbg} && $self->Debug("compile checksum $checksum");
	$self->{compile_checksum} = $checksum;
    }

    my $compile_checksum = $no_compile_checksum ? '' : $self->{compile_checksum};

    my @inode_stat = ();
    if($self->{inode_names}) {
	@inode_stat = stat($file);
	# one or the other device or file ids must be not 0
	unless($inode_stat[0] || $inode_stat[1]) {
	    @inode_stat = ();
	}
    }

    if(@inode_stat) {
	$id = sprintf("____DEV%X_INODE%X",@inode_stat[0,1]);
	$id .= 'x'.$compile_checksum;
    } else {
	if($abs_file) {
	    $file = $abs_file;
	}
	$file =~ s|/+|/|sg;
	$file =~ s/[\Wx]/_/sg;
	my $file_name_length = length($file);
	if($file_name_length >= 35) {
	    $id = substr($file, $file_name_length - 35, 36);
	    # only do the hex of the original file to create a unique identifier for the long id
	    $id .= 'x'.&md5_hex($file.$compile_checksum);
	} else {
	    $id = $file.'x'.$compile_checksum;
	}
    }

    $id = '__ASP_'.$id;
}

ASP.pm  view on Meta::CPAN

sub UndefRoutine {
    my($self, $subid) = @_;

    my $code = \&{$subid};
    if($code) {
	$self->{dbg} && $self->Debug("undefing sub $subid code $code");
	undef(&$code); # method for perl 5.6.1
	undef($code);  # older perls ??
    }
}

sub ReadFile {
    my($self, $file) = @_;

    local *READFILE;
    open(READFILE, $file) || $self->Error("can't open file $file for reading");
    local $/ = undef;
    my $data = <READFILE>;
    close READFILE;

    \$data;
}

# if the $file is an absolute path, then just return the file
# if the $file is a relative path, concat it with the passed in directory
sub AbsPath {
    my($file, $dir) = @_;

    # we test for first unix style and then win32 style path conventions
    if($file =~ m|^/| or $file =~ m|^.\:|) {
	$file;
    } else {
	# we only can absolute the path if the directory path is absolute
	if($dir =~ m|^/| or $dir =~ m|^.\:|) {
	    $file = $dir.'/'.$file;
	} else {
	    $file;
	}
    }
}       

sub CompilePerl {
    my($self, $script, $subid, $package) = @_;
    $package ||= $self->{GlobalASA}{'package'};
    $subid ||= '';

    ref($script) || die("no ref to perl script to compile");
    $subid && $self->UndefRoutine($subid);
    $self->{dbg} && $self->Debug("compiling into package $package subid [$subid]");    

    $self->{compile_perl_count}++; # counter used in test case closure.t

    my $eval = 
      join(" ;; ", 
	   "package $package;", # for no sub closure
	   "sub $subid { ",
	   "package $package;", # for sub closure
	   $$script,
	   '}',
	  );
#    $eval =~ tr///; # untaint
    $eval =~ /^(.*)$/s;
    $eval = $1;

    my $sub_ref;

    if($self->{use_strict}) { 
	local $SIG{__WARN__} = sub { die("maybe use strict error: ", @_) };

	# comment out for now, until 3.0 release for this may create lots
	# of compile time errors for people that will need to fix scripts
	#	local $^W = 1; # trigger my closure errors, --jc 9/7/2002
	$sub_ref = eval $eval;
    } else {
	local $SIG{__WARN__} = sub { $self->Out(@_) };
	$sub_ref = eval $eval;
    }

    my $rv; # for readability
    my $error = $@;

    if($@) {
	$self->CompileError($eval); # don't throw error, so we can throw die later
	$subid && $self->UndefRoutine($subid);
	$rv = undef;
    } else {
	if($subid) {
	    if(&config($self, 'RegisterIncludes')) {
		$self->RegisterIncludes($script);
	    }
	    $rv = $subid;
	} else {
	    $rv = $sub_ref;
	}
    }

    $@ = $error;
    $rv;
}

sub TestForSubs {
    my($self, $script) = @_;
    $$script =~ /(^|\n)\s*sub\s+([^\s\{]+)\s*\{/ ? 1 : 0;
}

sub InitPackageGlobals {
    my $self = shift;

    unless($self->{response_tied}) {
	# set printing to Response object
	$self->{response_tied} = 1;
	tie *RESPONSE, 'Apache::ASP::Response', $self->{Response};
	select(RESPONSE);
    }

    # ---- init package objects ----
    # unoptimized this because we should only call this function once
    # and maybe twice if there is a defined Script_OnStart
    for my $object (@Apache::ASP::Objects) {
	for my $import_package (@{$self->{init_packages}}) {
	    my $init_var = $import_package.'::'.$object;

ASP.pm  view on Meta::CPAN

	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;
    
    for(@modules) {
	if(defined $LoadedModules{$_}) {
	    if($LoadedModules{$_} == 0) {
		if($LoadModuleErrors{$category}) {
		    $self->Error("cannot load $_ for $category: $LoadModuleErrors{$category}; $@");
		} else {
		    $self->Debug("already failed to load $_");
		}
		$load_errors++;
	    } 
	    next;
	}

	$_ =~ tr///; # untaint
	eval "use $_";
	if($@) { 
	    if($LoadModuleErrors{$category}) {
		$self->Error("cannot load $_ for $category: $LoadModuleErrors{$category}; $@");
	    } else {
		# don't wan't Log() output for make test when optional modules aren't installed
		# is not installed, --jc 6/11/2001
		$self->Debug("cannot load $_ for $category: $@");
	    }
	    $load_errors++;
	    $LoadedModules{$_} = 0;
	} else {
	    $self->{dbg} && $self->Debug("loaded module $_");
	    $LoadedModules{$_} = 1;
	}
    }
    
    ! $load_errors;
}

sub Loader {
    # this is enough to load Apache::ASP::Load, we only need to do it
    # at runtime since the purpose of Loader() is to be run from 
    # the httpd.conf during parent startup time, so this module will
    # be cached just fine at that time.
    #
    require Apache::ASP::Load;
    &Apache::ASP::Load::Run(@_);
}

sub DSOError {
    my $r = shift;

    # this could happen with a bad filtering sequence
    warn(<<ERROR);
No valid request object ($r) passed to ASP handler

If you are getting this error message and are using mod_perl 1.x and Apache 1.x,
you likely have a broken DSO version of mod_perl which often occurs
when using RedHat RPMs.  One fix reported is to configure "PerlSendHeader On".
Another fix is to compile statically the apache + mod_perl build as
RedHat RPMs have been trouble.

If you are using a newer mod_perl2 + Apache2, make sure you have
upgraded to the last Apache::ASP release, and report the issue
if problems continue to the Apache::ASP mailing list.  As of
December 2002, mod_perl2 + Apache2 combination is still experimental
and under development.

Please check FAQ or mod_perl archives for more information.

ERROR
  ;

	500;
}

sub CompileChecksumKeys() { \@CompileChecksumKeys };

sub get_dir_config {

ASP.pm  view on Meta::CPAN

   should now work.  Thanks for bug report from David Kulp.

 + Added an early srand() for better $ServerID creation

 + Work around for DSO problems where $r is not always correctly 
   defined in Apache::ASP::handler().  Thanks to Tom Lear for patch.

=item $VERSION = 2.31; $DATE="01/22/2002";

 + $Server->MapInclude() API extension created to wrap up Apache::ASP::SearchDirs 
   functionality so one may do an conditional check for an include existence befor 
   executing $Response->Include().  Added API test to server.t

 + $Server->Transfer() now allows arguments like $Response->Include(), and now acts just
   as a wrapper for:

     $Response->Include($file, @args);
     $Response->End();

   added test case at t/server_transfer.t

 + Removed dependency of StatINC functionality on Apache::Symbol.  Apache::Symbol 
   is no longer required.  Added test of t/stat_inc.t for correct StatINC initialization
   for platforms where Devel::Symdump is present.

 + Better error message when $Request->Params has not been defined with RequestParams
   config & it gets used in script.  Added test case as t/request_params_none.t

 + Directories cannot now be included as scripts via $Response->Include(), added
   test case to t/include.t

 - No longer make $Response->Flush dependent on $Response->IsClientConnected() to 
   be true to write output to client.  There have been spurious errors reported
   about the new ( >= 2.25 ) IsClientConnected code, and this will limit the impact 
   of that functionality possibly not working still to those users explicitly using 
   that API.

 + $Response->AddHeader($header_name, $value) now will set $Response members
   for these headers: Content-Type, Cache-Control, Expires.  This is to avoid
   both the application & Apache::ASP sending out duplicate headers.  Added
   test cases for this to t/response.t

 + split up Bundle::Apache::ASP into that, and Bundle::Apache::ASP::Extra
   the former with just the required modules to run, and the latter 
   for extra functionality in Apache::ASP

 + new $Request->{Method} member to return $r->method of GET or POST that 
   client browser is requesting, added t/request.t sub test to cover this member.

=item $VERSION = 2.29; $DATE="11/19/2001";

 +Added some extra help text to the ./cgi/asp --help message
  to clarify how to pass arguments to a script from the command line.

 +When using $Server->Mail() API, if Content-Type header is set,
  and MIME-Version is not, then a "MIME-Version: 1.0" header will be sent
  for the email.  This is correct according to RFC 1521 which specifies
  for the first time the Content-Type: header for email documents.
  Thanks to Philip Mak for pointing out this correct behavior.

 +Made dependent on MLDBM::Sync version .25 to pass the taint_check.t test

 +Improved server_mail.t test to work with mail servers were relaying is denied

 +Added <html><body> tags to MailErrorsTo email

 --Fixed SessionCount / Session_OnEnd bug, where these things were not
  working for $Sessions that never had anything written to them.
  This bug was introduced in 2.23/2.25 release.

  There was an optimization in 2.23/2.25 where a $Session that was never
  used does not write its state lock file & dbm files to disk, only if
  it gets written too like $Session->{MARK}++.  Tracking of these NULL $Sessions 
  then is handled solely in the internal database.  For $Session garbage 
  collection though which would fire Session_OnEnd events and update 
  SessionCount, the Apache::ASP::State->GroupMembers() function was just 
  looking for state files on disk ... now it looks in the internal database 
  too for SessionID records for garbage collection.

  Added a test at ./t/session_events.t for these things.

 +Some optimizations for $Session API use.

 +Added support for XSLT via XML::LibXSLT, patch courtesy of Michael Buschauer

 -Got rid of an warning when recompiling changing includes under perl 5.6.1...
  undef($code) method did not work for this perl version, rather undef(&$code) does.
  Stopped using using Apache::Symbol for this when available.

 -Make Apache::ASP script run under perl taint checking -T for perl 5.6.1...
  $code =~ tr///; does not work to untaint here, so much use the slower:
  $code =~ /^(.*)$/s; $code = $1; method to untaint.

 -Check for inline includes changing, included in a dynamic included
  loaded at runtime via $Response->Include().  Added test case for
  this at t/include_change.t.  If an inline include of a dynamic include
  changes, the dynamic include should get recompiled now.

 -Make OK to use again with PerlTaintCheck On, with MLDBM::Sync 2.25.
  Fixed in ASP.pm, t/global.asa, and created new t/taint_check.t test script

 +Load more modules when Apache::ASP is loaded so parent will share more
  with children httpd: 
   Apache::Symbol 
   Devel::Symdump 
   Config 
   lib 
   MLDBM::Sync::SDBM_File

 +When FileUploadMax bytes is exceeded for a file upload, there will not
  be an odd error anymore resulting from $CGI::POST_MAX being triggered,
  instead the file upload input will simply be ignored via $CGI::DISABLE_UPLOADS.
  This gives the developer the opportunity to tell the user the the file upload
  was too big, as demonstrated by the ./site/eg/file_upload.asp example.

  To not let the web client POST a lot of data to your scripts as a form
  of a denial of service attack use the apache config LimitRequestBody for the 
  max limits.  You can think of PerlSetVar FileUploadMax as a soft limit, and 
  apache's LimitRequestBody as a hard limit.

 --Under certain circumstances with file upload, it seems that IsClientConnected() 
  would return an aborted client value from $r->connection->aborted, so
  the buffer output data would not be flushed to the client, and 
  the HTML page would return to the browser empty.  This would be under
  normal file upload use.  One work-around was to make sure to initialize
  the $Request object before $Response->IsClientConnected is called,
  then $r->connection->aborted returns the right value.
  
  This problem was probably introduced with IsClientConnected() code changes
  starting in the 2.25 release.

=item $VERSION = 2.27; $DATE="10/31/2001";

 + Wrapped call to $r->connection->fileno in eval {} so to 
   preserve backwards compatibility with older mod_perl versions
   that do not have this method defined.  Thanks to Helmut Zeilinger
   for catching this.

 + removed ./dev directory from distribution, useless clutter

 + Removed dependency on HTTP::Date by taking code into
   Apache::ASP as Apache::ASP::Date.  This relieves
   the dependency of Apache::ASP on libwww LWP libraries.
   If you were using HTTP::Date functions before without loading
   "use HTTP::Date;" on your own, you will have to do this now.

 + Streamlined code execution.  Especially worked on 
   $Response->IsClientConnected which gets called during
   a normal request execution, and got rid of IO::Select
   dependency. Some function style calls instead of OO style 
   calls where private functions were being invokes that one 
   would not need to override.

 - Fixed possible bug when flushing a data buffer where there
   is just a '0' in it.

 + Updated docs to note that StateCache config was deprecated
   as of 2.23.  Removed remaining code that referenced the config.

 + Removed references to unused OrderCollections code.

ASP.pm  view on Meta::CPAN


 +UseStrict setting compiles all scripts including
  global.asa with "use strict" turned on for catching
  more coding errors.  With this setting enabled,
  use strict errors die during compilation forcing
  Apache::ASP to try to recompile the script until
  successful.

 -Object use in includes like $Response->Write() 
  no longer error with "use strict" programming.  

 +SessionQuery config setting with $Server->URL($url, { %params } ) 
  alpha API extensions to enable cookieless sessions.

 +Debugging not longer produces internal debugging
  by default.  Set to -1,-2 for internal debugging
  for Debug settings 1 & 2.

 +Both StateSerializer & StateDB can be changed 
  without affecting a live web site, by storing 
  the configurations for $Application & $Session 
  in an internal database, so that if $Session was
  created with SDBM_File for the StateDB (default),
  it will keep this StateDB setting until it ends.

 +StateSerializer config setting.  Default Data::Dumper,
  can also be set to Storable.  Controls how data is
  serialized before writing to $Application & $Session.

 +Beefed up the make test suite.

 +Improved the locking, streamlining a bit of the 
  $Application / $Session setup process.  Bench is up to 
  22 from 21 hits / sec on dev NT box.

 +Cut more fat for faster startup, now on my dev box 
  I get 44 hits per sec Apache::ASP vs. 48 Embperl 
  vs. 52 CGI via Apache::Registry for the HelloWorld Scripts.

 -Improved linking for the online site documentation, 
  where a few links before were bad.

=item $VERSION = 0.17; $DATE="11/15/99";

 ++20%+ faster startup script execution, as measured by the 
  HelloWorld bench.  I cut a lot of the fat out of 
  the code, and is now at least 20% faster on startup 
  both with and without state.

  On my dev (NT, apache 1.3.6+mod_perl) machine, I now get:

	42 hits per sec on Apache::ASP HelloWorld bench
	46 hits per sec on Embperl (1.2b10) and
	51 hits per sec for CGI Apache::Registry scripts  

  Before Apache::ASP was clocking some 31 hits per sec.
  Apache::ASP also went from 75 to 102 hits per second 
  on Solaris.

 +PerlTaintCheck On friendly.  This is mod_perl's way 
  of providing -T taint checking.  When Apache::ASP
  is used with state objects like $Session or $Application,
  MLDBM must also be made taint friendly with:

    $MLDBM::RemoveTaint = 1;

  which could be put in the global.asa.  Documented.

 +Added $Response->ErrorDocument($error_code, $uri_or_string) 
  API extension which allows for setting of Apache's error
  document at runtime.  This is really just a wrapper 
  for Apache->custom_response() renamed so it syncs with
  the Apache ErrorDocument config setting.  Updated
  documentation, and added error_document.htm example.

 =OrderCollections setting was added, but then REMOVED
  because it was not going to be used.  It bound 
  $Request->* collections/hashes to Tie::IxHash, so that data
  in those collections would be read in the order the 
  browser sent it, when eaching through or with keys.

 -global.asa will be reloaded when changed.  This broke
  when I optimized the modification times with (stat($file))[9]
  rather than "use File::stat; stat($file)->mtime"

 -Make Apache::ASP->Loader() PerlRestartHandler safe,
  had some unstrict code that was doing the wrong thing.

 -IncludesDir config now works with DynamicIncludes.

 +DebugBufferLength feature added, giving control to 
  how much buffered output gets shown when debugging errors.

 ++Tuning of $Response->Write(), which processes all
  static html internally, to be almost 50% faster for
  its typical use, when BufferingOn is enabled, and 
  CgiHeaders are disabled, both being defaults.

  This can show significant speed improvements for tight
  loops that render ASP output.

 +Auto linking of ./site/eg/ text to example scripts
  at web site.

 +$Application->GetSession($session_id) API extension, useful
  for managing active user sessions when storing session ids
  in $Application.  Documented.

 -disable use of flock() on Win95/98 where it is unimplemented

 -@array context of $Request->Form('name') returns
  undef when value for 'name' is undefined.  Put extra
  logic in there to make sure this happens. 

=item $VERSION = 0.16; $DATE="09/22/99";

 -$Response->{Buffer} and PerlSetVar BufferingOn
  configs now work when set to 0, to unbuffer output,
  and send it out to the web client as the script generates it.

  Buffering is enabled by default, as it is faster, and
  allows a script to error cleanly in the middle of execution.  



( run in 1.089 second using v1.01-cache-2.11-cpan-39bf76dae61 )