Apache-Scriptor
view release on metacpan or search on metacpan
Scriptor.pm view on Meta::CPAN
package Apache::Scriptor;
$VERSION="1.21";
use CGI::WebOut;
use Cwd;
# constructor new()
# Ñîçäàåò íîâûé Apache::Scriptor-îáúåêò.
sub new
{ my ($class)=@_;
my $this = {
Handlers => {},
HandDir => ".",
htaccess => ".htaccess",
# Çàïîìèíàåì, êàêîé çàïðîñ â äåéñòâèòåëüíîñòè áûë âûïîëíåí, ÷òîáû
# ïîòîì èñêàòü åãî â htaccess-àõ.
self_scriptname => $ENV{SCRIPT_NAME}
};
return bless($this,$class);
}
# void set_handlers_dir(string $dir)
# Óñòàíàâëèâàåò äèðåêòîðèþ äëÿ ïîèñêà îáðàáîò÷èêîâ.
sub set_handlers_dir
{ my ($this,$dir)=@_;
$this->{HandDir}=$dir;
}
# void addhandler(ext1=>[h1, h2,...], ext2=>[...])
# Óñòàíàâëèâàåò îáðàáîò÷èê(è) äëÿ ðàñøèðåíèé ext1 è ext2.
# Çäåñü h1, h2 è ò.ä. ïðåäñòàâëÿþò ñîáîé ÑÑÛËÊÈ íà ôóíêöèè-îáðàáîò÷èêè.
# Åñëè æå îíè çàäàíû íå êàê ññûëêè, à êàê ÑÒÐÎÊÈ, òî â ìîìåíò îáðàùåíèÿ
# ê î÷åðåäíîìó îáðàáîò÷èêó ïðîèçâîäèòñÿ ïîïûòêà åãî çàãðóçèòü èç ôàéëà,
# èìÿ êîòîðîãî ñîâïàäàåò ñ èìåíåì îáðàáîò÷èêà ñ ðàñøèðåíèåì ".pl" èç
# äèðåêòîðèè, êîòîðàÿ çàäàíà âûçîâîì set_handlers_dir().
sub addhandler
{ my ($this,%hands)=@_;
%{$this->{Handlers}}=(%{$this->{Handlers}},%hands);
return;
}
# void pushhandler(string ext, func &func)
# Äîáàâëÿåò îáðàáîò÷èê äëÿ ðàñøèðåíèÿ ext â êîíåö ñïèñêà îáðàáîò÷èêîâ.
sub pushhandler
{ my ($this,$ext,$func)=@_;
$this->{Handlers}{$ext}||=[];
push(@{$this->{Handlers}{$ext}},$func);
return;
}
# void removehandler(ext1, ext2, ...)
# Óäàëÿåò îáðàáîò÷èê(è) äëÿ ðàñøèðåíèé ext1 è ext2.
sub removehandler
{ my ($this,@ext)=@_;
foreach (@ext) { delete $this->{Handlers}{$_} }
return;
}
# void set_404_url($url)
# Óñòàíàâëèâàåò àäðåñ ñòðàíèöû 404-é îøèáêè, íà êîòîðóþ áóäåò ïðîèçâåäåí
# ðåäèðåêò, åñëè ôàéë íå íàéäåí.
sub set_404_url
{ my ($th,$url)=@_;
$th->{404}=$url;
}
# void set_htaccess_name($name)
# Óñòàíàâëèâàåò èìÿ htaccess-ôàéëà. Ïî óìîë÷àíèþ ýòî .htaccess.
sub set_htaccess_name
{ my ($th,$htaccess)=@_;
$th->{htaccess}=$htaccess;
}
sub process_htaccess
{ my ($th,$fname)=@_;
open(local *F,$fname) or return;
# Ñíà÷àëà ñîáèðàåì âñå äèðåêòèâû èç .htaccess
my %Action=();
my @AddHandler=();
while(!eof(F)) {
my $s=<F>; $s=~s/^\s+|#.*|\s+$//sg; next if $s eq "";
# Äèðåêòèâà Action
if($s=~m/Action\s+([\w\d-]+)\s*"?([^"]+)"?/si) {
$Action{$1}=1 if $2 eq $th->{self_scriptname};
}
# Äèðåêòèâà AddHandler
if($s=~m/AddHandler\s+([\w\d-]+)\s*(.+)/si) {
push @AddHandler, [ $1, [ map { s/^\s*\.?|\s+$//sg; $_?($_):() } split /\s+/, $2 ] ];
}
# Äèðåêòèâà ErrorDocument 404
if($s=~/ErrorDocument\s+404\s+"?([^"]+)"?/si) {
$th->set_404_url($1);
}
}
# Çàòåì äîáàâëÿåì öåïî÷êè îáðàáîò÷èêîâ
my %ProcessedExt=();
foreach my $info (@AddHandler) {
my ($hand,$ext)=@$info;
# Ñðàçó îòìåòàåì îáðàáîò÷èêè, êîòîðûå ÍÅ óêàçûâàþò íà Apache::Scriptor.
# Ìû íå ìîãëè ýòîãî ñäåëàòü â âåðõíåì öèêëå, ïîòîïìó ÷òî äèðåêòèâû
# Action è AddHandler ìîãóò èäòè íå ïî ïîðÿäêó.
next if !$Action{$hand};
# Äîáàâëÿåì äëÿ êàæäîãî ðàñøèðåíèÿ îáðàáîò÷èê â öåïî÷êó
foreach my $ext (@$ext) {
# Åñëè ýòî ðàñøèðåíèå âñòðå÷àåòñÿ â òåêóùåì htaccess-ôàéëå
# âïåðâûå, ýòî çíà÷èò, ÷òî íà÷àòà î÷åðåäíàÿ öåïî÷êà îáðàáîò÷èêîâ.
#  ýòîì ñëó÷àå íóæíî óäàëèòü óæå èìåþùóþñÿ öåïî÷êó.
if(!$ProcessedExt{$ext}) {
$th->removehandler($ext);
$ProcessedExt{$ext}=1;
}
# Çàòåì ñïîêîéíî âûçûâàåì pushhandler()
$th->pushhandler($ext,$hand);
}
}
}
sub process_htaccesses
{ my ($th,$path)=@_;
# Ñíà÷àëà îïðåäåëÿåì âñå ïîëíûå ïóòè ê htaccess-ôàéëàì
my @Hts=();
while($path=~m{[/\\]}) {
if(-d $path) {
my $ht="$path/$th->{htaccess}";
unshift(@Hts,$ht) if -f $ht;
}
$path=~s{[/\\][^/\\]*$}{}s;
}
# Çàòåì îáðàáàòûâàåì ýòè ôàéëû, íà÷èíàÿ ñ ñàìîãî êîðíåâîãî
map { $th->process_htaccess($_) } @Hts;
}
# void run_uri(string $uri [,string $path_translated])
# Çàïóñêàåò óêàçàííûé URI íà îáðàáîòêó. Åñëè óêàçàí ïàðàìåòð $path_translated,
# òî îí ñîäåðæèò ïîëíîå èìÿ ôàéëà ñ ñîäåðæèìûì äëÿ îáðàáîòêè. Â ïðîòèâíîì
# ñëó÷àå èìÿ ôàéëà âû÷èñëÿåòñÿ àâòîìàòè÷åñêè íà îñíîâå $uri (ýòî íå âñåãäà
# ðàáîòàåò ïðàâèëüíî - íàïðèìåð, òàêàÿ øòóêà íå ïðîéäåò, åñëè äèðåêòîðèÿ áûëà
# çàâåäåíà êàê Alias Apache).
sub run_uri
{ my ($this,$uri,$path)=@_;
Header("X-Powered-by: Apache::Scriptor v$VERSION. (C) Dmitry Koterov <koterov at cpan dot org>") if !$CopySend++;
# Òåïåðü ðàáîòàåì ñ ÊÎÏÈÅÉ îáúåêòà. Òàêèì îáðàçîì, äàëüíåéøèå âûçîâû
# process_htaccesses è ò.ä. íå îòðàçÿòñÿ íà îáùåì ñîñòîÿíèè îáúåêòà
# ïîñëå îêîí÷àíèÿ çàïðîñà.
local $this->{Handlers}={%{$this->{Handlers}}};
local $this->{404}=$this->{404};
# Ðàçäåëÿåì íà URL è QUERY_STRING
local ($ENV{SCRIPT_NAME},$q) = split /\?/, $uri, 2;
$ENV{QUERY_STRING}=defined $q? $q : "";
# Âû÷èñëÿåì ïóòü ê ôàéëó ñêðèïòà ïî URI
if(!$path) {
$path="$ENV{DOCUMENT_ROOT}$ENV{SCRIPT_NAME}";
}
# Ãîòîâèì íîâûå ïåðåìåííûå îêðóæåíèÿ, ÷òîáû ñêðûòü Apache::Scriptor;
local $ENV{REQUEST_URI} = $uri;
local $ENV{SCRIPT_FILENAME} = $path;
local $ENV{REDIRECT_URL}; delete($ENV{REDIRECT_URL});
local $ENV{REDIRECT_STATUS}; delete($ENV{REDIRECT_STATUS});
# Ìåíÿåì òåêóùóþ äèðåêòîðèþ.
my $MyDir=getcwd();
($MyDir) = $MyDir=~/(.*)/;
my ($dir) = $path; $dir=~s{(.)[/\\][^/\\]*$}{$1}sg;
chdir($dir); getcwd(); # getcwd: Ñáðàñûâàåò $ENV{PWD}. Íàì ýòî íàäî? Ôèã çíàåò...
# Îáðàáàòûâàåì ôàéëû .htaccess.
$this->process_htaccesses($path);
# Âñå. Òåïåðü ñîñòîÿíèå ïåðåìåííûõ ñêðèïòà òàêîå æå, êàê ó ñòðàíèöû,
# êîòîðàÿ â äàëüíåéøåì ïîëó÷èò óïðàâëåíèå. Çàïóñêàåì îáðàáîò÷èêè.
$this->__run_handlers();
# Âîññòàíàâëèâàåì òåêóùóþ äèðåêòîðèþ
chdir($MyDir); getcwd();
}
# Âíóòðåííÿÿ ôóíêöèÿ - çàïóñêàåò îáðàáîò÷èêè äëÿ ôàéëà, êîòîðûé çàäàí â %ENV.
# Âûçûâàåòñÿ Â ÊÎÍÒÅÊÑÒÅ ÝÒÎÃÎ ÔÀÉËÀ (òî åñòü, %ENV íàõîäèòñÿ â òàêîì æå ñîñòîÿíèè,
# êàê ïîñëå îáÿ÷íîãî çàïóñêà ñêðèïòà Àïà÷åì, è òåêóùàÿ äèðåêòîðèÿ ñîîòâåòñòâóåò
# äèðåêòîðèè ñî ñòðàíèöåé).
sub __run_handlers
{ my ($th)=@_;
# ðàñøèðåíèå ôàéëà
my ($ext) = $ENV{SCRIPT_FILENAME}=~m|\.([^.]*)$|; if(!defined $ext) { $ext=""; }
# âûáèðàåì ñïèñîê îáðàáîò÷èêîâ äëÿ ýòîãî ðàñøèðåíèÿ
$th->{Handlers}{$ext}
or die "$ENV{SCRIPT_NAME}: could not find handlers chain for extension \"$ext\"\n";
# âõîäíîé áóôåð (âíà÷àëå â íåì ñîäåðæèìîå ôàéëà, åñëè äîñòóïíî)
my $input="";
if(open(local *F, $ENV{SCRIPT_FILENAME})) { local ($/,$\); binmode(F); $input=<F>; }
# ïðîõîäèìñÿ ïî âñåì îáðàáîò÷èêàì
my $next=1; # íîìåð ñëåäóþùåãî îáðàáîò÷èêà
my @hands=@{$th->{Handlers}{$ext}};
NoAutoflush() if @hands>1;
foreach my $hand (@hands)
{ # Îáúåêò ïåðåíàïðàâëåíèÿ âûâîäà. Åñëè ó íàñ âñåãî îäèí îáðàáîò÷èê, òî
# ïåðåíàïðàâëÿòü âûâîä íå ïîòðåáóåòñÿ. Èíà÷å - ïîòðåáóåòñÿ, ÷òî è äåëàåòñÿ
my $OutObj=$hands[$next++]? CGI::WebOut->new : undef;
my $func=$hand; # óêàçàòåëü íà ôóíêöèþ
# Ïðîâåðÿåì - íóæíî ëè çàãðóçèòü îáðàáîò÷èê?
if((ref($func)||"") ne "CODE") {
# ïåðåêëþ÷àåì ïàêåò
package Apache::Scriptor::Handlers;
# îáðàáîò÷èêà åùå íåò â ýòîì ïàêåòå?
if(!*{$func}{CODE}) {
my $hname="$th->{HandDir}/$func.pl";
-f $hname or die "$ENV{SCRIPT_NAME}: could not load the file $hname for handler $hand\n";
do "$hname";
*{$func}{CODE} or die "$ENV{SCRIPT_NAME}: cannot find handler $hand in $hname after loading $hname\n";
}
# ïîëó÷àåì óêàçàòåëü íà ôóíêöèþ îáðàáîò÷èêà
local $this=$th;
$func=*{$func}{CODE};
}
# Ôóíêöèÿ îáðàáîò÷èêà ïðèíèìàåò ïàðàìåòð: âõîäíîé áóôåð.
# Åå çàäà÷à - îáðàáîòàòü åãî è, èñïîëüçóÿ print, ïðîïå÷àòàòü ðåçóëüòàò.
#  ñëó÷àå îøèáêè (ôàéë íå íàéäåí) ôóíêöèÿ äîëæíà âîçâðàòèòü -1!
my $result=&$func($input);
if($result eq "-1") {
if($th->{404} && $th->{404} ne $th->{self_scriptname}) {
Redirect($th->{404});
exit;
} else {
die "$hand: could not find the file $ENV{SCRIPT_FILENAME}\n";
}
}
# Òî, ÷òî ïîëó÷èëîñü, êëàäåì âî âõîäíîé áóôåð äëÿ ñëåäóþùåãî îáðàáîò÷èêà.
# Åñëè âûâîä íå ïåðåíàïðàâëÿëñÿ, òî êëàäåì òóäà "".
$input=$OutObj?$OutObj->buf:"";
}
# Îêîí÷àòåëüíûé ðåçóëüòàò îêàæåòñÿ âî âõîäíîì áóôåðå (êàê áóäòî ãîòîâûé äëÿ
# ñëåäóþùåãî îáðàáîò÷èêà, êîòîðîãî íåò). Åãî-òî ìû è âûâîäèì â áðàóçåð.
print $input;
}
package Apache::Scriptor::Handlers;
use CGI::WebOut;
#  ýòîì ïàêåòå ïåðå÷èñëÿþòñÿ ñòàíäàðòíûå îáðàáîò÷èêè,
# êîòîðûå, ñêîðåå âñåãî, áóäóò èñïðîëüçîâàíû â ïåðâóþ î÷åðåäü.
# Èìåííî â ýòîò ïàêåò ïîïàäàþò îáðàáîò÷èêè, çàãðóæåííûå àâòîìàòè÷åñêè.
# Îáðàáîò÷èê ïî óìîë÷àíèþ - ïðîñòî âûâîäèò òåêñò
sub default
{ my ($input,$fname)=@_;
-f $ENV{SCRIPT_FILENAME} or return -1;
CGI::WebOut::Header("Content-type: text/html");
print $input;
}
# Îáðàáîò÷èê perl-ñêðèïòîâ. Ïîäðàçóìåâàåòñÿ, ÷òî âûâîä ñêðèïòà èäåò ÷åðåç print.
sub perl
{ my ($input)=@_;
-f $ENV{SCRIPT_FILENAME} or return -1;
eval("\n#line 1 \"$ENV{SCRIPT_NAME}\"\npackage main; $input");
}
return 1;
__END__
=head1 NAME
Apache::Scriptor - Support for Apache handlers conveyor.
=head1 SYNOPSIS
Synopsis are not so easy as in other modules, that's why let's see example below.
=head1 FEATURES
=over 4
=item *
Uses ONLY perl binary.
=item *
Helps to organize the Apache handler conveyor. That means you can redirect the output from one handler to another handler.
=item *
Supports non-existance URL handling and 404 Error processing.
=item *
Uses C<.htaccess> files to configure.
=back
=head1 EXAMPLE
### Consider the server structure:
### /
### _Kernel/
### handlers/
### s_copyright.pl
### ...
### .htaccess
### Scriptor.pl
### .htaccess
### test.htm
### File /.htaccess:
# Setting up the conveyor for .htm:
# "input" => eperl => s_copyright => "output"
Action perl "/_Kernel/Scriptor.pl"
AddHandler perl .htm
Action s_copyright "/_Kernel/Scriptor.pl"
AddHandler s_copyright .htm
### File /_Kernel/.htaccess:
# Enables Scriptor.pl as perl executable
Options ExecCGI
AddHandler cgi-script .pl
### File /_Kernel/Scriptor.pl:
#!/usr/local/bin/perl -w
use FindBin qw($Bin); # òåêóùàÿ äèðåêòîðèÿ
my $HandDir="$Bin/handlers"; # äèðåêòîðèÿ ñ îáðàáîò÷èêàìè
# This is run not as CGI-script?
if(!$ENV{DOCUMENT_ROOT} || !$ENV{SCRIPT_NAME} || !$ENV{SERVER_NAME}) {
print "This script has to be used only as Apache handler!\n\n";
exit;
}
# Non-Apache-handler run?
if(!$ENV{REDIRECT_URL}) {
print "Location: http"."://$ENV{SERVER_NAME}/\n\n";
exit;
}
require Apache::Scriptor;
my $Scr=Apache::Scriptor->new();
# Setting up the handlers' directory.
$Scr->set_handlers_dir($HandDir);
# Go on!
$Scr->run_uri($ENV{REQUEST_URI},$ENV{PATH_TRANSLATED});
### File /_Kernel/handlers/s_copyright.pl:
sub s_copyright
{ my ($input)=@_;
-f $ENV{SCRIPT_FILENAME} or return -1; # Error indicator
Scriptor.pm view on Meta::CPAN
This module is used to handle all the requests through the Perl script
(such as C</_Kernel/Scriptor.pl>, see above). This script is just calling
the handlers conveyor for the specified file types.
When you place directives like these in your C<.htaccess> file:
Action s_copyright "/_Kernel/Scriptor.pl"
AddHandler s_copyright .htm
Apache sees that, to process C<.htm> document, C</_Kernel/Scriptor.pl> handler
should be used. Then, Apache::Scriptor starts, reads this C<.htaccess> and remembers
the handler name for C<.htm> document: it is C<s_copyright>. Apache::Scriptor searches
for C</_Kernel/handlers/s_copyright.pl>, trying to find the subroutine with the same name:
C<s_copyright()>. Then it runs that and passes the document body, returned from the previous
handler, as the first parameter.
How to start the new conveyor for extension C<.html>, for example? It's easy: you
place some Action-AddHandler pairs into the C<.htaccess> file. You must choose
the name for these handlers corresponding to the Scriptor handler file names
(placed in C</_Kernel/handlers>). Apache does NOT care about these names, but
Apache::Scriptor does. See example above (it uses two handlers: built-in C<perl> and user-defined C<s_copyright>).
=head1 DESCRIPTION
=over 11
=item C<require Apache::Scriptor>
Loads the module core.
=item C<Apache::Scriptor'new>
Creates the new Apache::Scriptor object. Then you may set up its
properties and run methods (see below).
=item C<$obj'set_handlers_dir($dir)>
Sets up the directory, which is used to search for handlers.
=item C<$obj'run_uri($uri [, $filename])>
Runs the specified URI through the handlers conveyer and prints out
the result. If C<$filename> parameter is specified, module does not
try to convert URL to filename and uses it directly.
=item C<$obj'addhandler(ext1=>[h1, h2,...], ext2=>[...])>
Manually sets up the handlers' conveyor for document extensions.
Values of C<h1>, C<h2> etc. could be code references or
late-loadable function names (as while parsing the C<.htaccess> file).
=item C<$obj'pushhandler($ext, $handler)>
Adds the handler C<$handler> th the end of the conveyor for extension C<$ext>.
=item C<$obj'removehandler($ext)>
Removes all the handlers for extension C<$ext>.
=item C<$obj'set_404_url($url)>
Sets up the redirect address for 404 error. By default, this value is
bringing up from C<.htaccess> files.
=item C<$obj'set_htaccess_name($name)>
Tells Apache::Scriptor object then Apache user configuration file is called C<$name>
(by default C<$name=".htaccess">).
=item C<$obj'process_htaccess($filename)>
Processes all the directives in the C<.htaccess> file C<$filename> and adds
all the found handlers th the object.
=item C<package Apache::Scriptor::Handlers>
This package holds ALL the handler subroutines. You can place
some user-defined handlers into it before loading the module to
avoid their late loading from handlers directory.
=back
=head1 AUTHOR
Dmitry Koterov <koterov at cpan dot org>, http://www.dklab.ru
=head1 SEE ALSO
C<CGI::WebOut>.
=cut
( run in 0.822 second using v1.01-cache-2.11-cpan-39bf76dae61 )