X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FPlugin%2FStatic%2FSimple.pm;h=73a98fe612da03b2f2a4bf0a6dfde2de339e0cc1;hb=b108737b2d548e40e9aa80dfb2405b5d921e61c3;hp=827b2523d751f7faff48dbcfcf69a7f967ebe846;hpb=40938a7c3697af8b5033f1046eede1673fafb442;p=catagits%2FCatalyst-Plugin-Static-Simple.git diff --git a/lib/Catalyst/Plugin/Static/Simple.pm b/lib/Catalyst/Plugin/Static/Simple.pm index 827b252..73a98fe 100644 --- a/lib/Catalyst/Plugin/Static/Simple.pm +++ b/lib/Catalyst/Plugin/Static/Simple.pm @@ -13,22 +13,20 @@ our $VERSION = '0.21'; __PACKAGE__->mk_accessors( qw/_static_file _static_debug_message/ ); -my $CONFIG_KEY = "Plugin::Static::Simple"; - sub prepare_action { my $c = shift; my $path = $c->req->path; - my $config = $c->config->{$CONFIG_KEY} ||= $c->config->{static}; - + my $config = $c->config->{static}; + $path =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; # is the URI in a static-defined path? foreach my $dir ( @{ $config->{dirs} } ) { my $dir_re = quotemeta $dir; - + # strip trailing slashes, they'll be added in our regex $dir_re =~ s{/$}{}; - + my $re = ( $dir =~ m{^qr/}xms ) ? eval $dir : qr{^${dir_re}/}; if ($@) { $c->error( "Error compiling static dir regex '$dir': $@" ); @@ -45,23 +43,23 @@ sub prepare_action { } } } - + # Does the path have an extension? if ( $path =~ /.*\.(\S{1,})$/xms ) { # and does it exist? $c->_locate_static_file( $path ); } - + return $c->next::method(@_); } sub dispatch { my $c = shift; - + return if ( $c->res->status != 200 ); - + if ( $c->_static_file ) { - if ( $c->config->{$CONFIG_KEY}{no_logs} && $c->log->can('abort') ) { + if ( $c->config->{static}{no_logs} && $c->log->can('abort') ) { $c->log->abort( 1 ); } return $c->_serve_static; @@ -73,26 +71,22 @@ sub dispatch { sub finalize { my $c = shift; - + # display all log messages - if ( $c->config->{$CONFIG_KEY}{debug} && scalar @{$c->_debug_msg} ) { + if ( $c->config->{static}{debug} && scalar @{$c->_debug_msg} ) { $c->log->debug( 'Static::Simple: ' . join q{ }, @{$c->_debug_msg} ); } - + return $c->next::method(@_); } sub setup { my $c = shift; - + $c->maybe::next::method(@_); - - if ( Catalyst->VERSION le '5.33' ) { - require File::Slurp; - } - - my $config = $c->config->{$CONFIG_KEY} ||= {}; - + + my $config = $c->config->{static} ||= {}; + $config->{dirs} ||= []; $config->{include_path} ||= [ $c->config->{root} ]; $config->{mime_types} ||= {}; @@ -101,11 +95,11 @@ sub setup { $config->{debug} ||= $c->debug; $config->{no_logs} = 1 unless defined $config->{no_logs}; $config->{no_logs} = 0 if $config->{logging}; - + # load up a MIME::Types object, only loading types with # at least 1 file extension $config->{mime_types_obj} = MIME::Types->new( only_complete => 1 ); - + # preload the type index hash so it's not built on the first request $config->{mime_types_obj}->create_type_index; } @@ -114,20 +108,20 @@ sub setup { # Based on Template Toolkit INCLUDE_PATH code sub _locate_static_file { my ( $c, $path, $in_static_dir ) = @_; - + $path = File::Spec->catdir( - File::Spec->no_upwards( File::Spec->splitdir( $path ) ) + File::Spec->no_upwards( File::Spec->splitdir( $path ) ) ); - - my $config = $c->config->{$CONFIG_KEY}; + + my $config = $c->config->{static}; my @ipaths = @{ $config->{include_path} }; my $dpaths; my $count = 64; # maximum number of directories to search - + DIR_CHECK: while ( @ipaths && --$count) { my $dir = shift @ipaths || next DIR_CHECK; - + if ( ref $dir eq 'CODE' ) { eval { $dpaths = &$dir( $c ) }; if ($@) { @@ -139,7 +133,7 @@ sub _locate_static_file { } else { $dir =~ s/(\/|\\)$//xms; if ( -d $dir && -f $dir . '/' . $path ) { - + # Don't ignore any files in static dirs defined with 'dirs' unless ( $in_static_dir ) { # do we need to ignore the file? @@ -151,7 +145,7 @@ sub _locate_static_file { next DIR_CHECK; } } - + # do we need to ignore based on extension? for my $ignore_ext ( @{ $config->{ignore_extensions} } ) { if ( $path =~ /.*\.${ignore_ext}$/ixms ) { @@ -161,20 +155,20 @@ sub _locate_static_file { } } } - + $c->_debug_msg( 'Serving ' . $dir . '/' . $path ) if $config->{debug}; return $c->_static_file( $dir . '/' . $path ); } } } - + return; } sub _serve_static { my $c = shift; - + my $full_path = shift || $c->_static_file; my $type = $c->_ext_to_type( $full_path ); my $stat = stat $full_path; @@ -183,32 +177,24 @@ sub _serve_static { $c->res->headers->content_length( $stat->size ); $c->res->headers->last_modified( $stat->mtime ); - if ( Catalyst->VERSION le '5.33' ) { - # old File::Slurp method - my $content = File::Slurp::read_file( $full_path ); - $c->res->body( $content ); + my $fh = IO::File->new( $full_path, 'r' ); + if ( defined $fh ) { + binmode $fh; + $c->res->body( $fh ); } else { - # new method, pass an IO::File object to body - my $fh = IO::File->new( $full_path, 'r' ); - if ( defined $fh ) { - binmode $fh; - $c->res->body( $fh ); - } - else { - Catalyst::Exception->throw( - message => "Unable to open $full_path for reading" ); - } + Catalyst::Exception->throw( + message => "Unable to open $full_path for reading" ); } - + return 1; } sub serve_static_file { my ( $c, $full_path ) = @_; - my $config = $c->config->{$CONFIG_KEY} ||= {}; - + my $config = $c->config->{static} ||= {}; + if ( -e $full_path ) { $c->_debug_msg( "Serving static file: $full_path" ) if $config->{debug}; @@ -227,12 +213,12 @@ sub serve_static_file { # looks up the correct MIME type for the current file extension sub _ext_to_type { my ( $c, $full_path ) = @_; - - my $config = $c->config->{$CONFIG_KEY}; - + + my $config = $c->config->{static}; + if ( $full_path =~ /.*\.(\S{1,})$/xms ) { my $ext = $1; - my $type = $config->{mime_types}{$ext} + my $type = $config->{mime_types}{$ext} || $config->{mime_types_obj}->mimeTypeOf( $ext ); if ( $type ) { $c->_debug_msg( "as $type" ) if $config->{debug}; @@ -253,15 +239,15 @@ sub _ext_to_type { sub _debug_msg { my ( $c, $msg ) = @_; - + if ( !defined $c->_static_debug_message ) { $c->_static_debug_message( [] ); } - + if ( $msg ) { push @{ $c->_static_debug_message }, $msg; } - + return $c->_static_debug_message; } @@ -274,8 +260,9 @@ Catalyst::Plugin::Static::Simple - Make serving static pages painless. =head1 SYNOPSIS - use Catalyst; - MyApp->setup( qw/Static::Simple/ ); + package MyApp; + use Catalyst qw/ Static::Simple /; + MyApp->setup; # that's it; static content is automatically served by Catalyst # from the application's root directory, though you can configure # things or bypass Catalyst entirely in a production environment @@ -299,6 +286,18 @@ through Catalyst. Note that actions mapped to paths using periods (.) will still operate properly. +If the plugin can not find the file, the request is dispatched to your +application instead. This means you are responsible for generating a +C<404> error if your applicaton can not process the request: + + # handled by static::simple, not dispatched to your application + /images/exists.png + + # static::simple will not find the file and let your application + # handle the request. You are responsible for generating a file + # or returning a 404 error + /images/does_not_exist.png + Though Static::Simple is designed to work out-of-the-box, you can tweak the operation by adding various configuration options. In a production environment, you will probably want to use your webserver to deliver @@ -326,8 +325,8 @@ Logging of static files is turned off by default. =head1 ADVANCED CONFIGURATION Configuration is completely optional and is specified within -Cconfig-E{'Plugin::Static::Simple'}>. If you use any of these -options, this module will probably feel less "simple" to you! +Cconfig-E{static}>. If you use any of these options, +this module will probably feel less "simple" to you! =head2 Enabling request logging @@ -335,7 +334,7 @@ Since Catalyst 5.50, logging of static requests is turned off by default; static requests tend to clutter the log output and rarely reveal anything useful. However, if you want to enable logging of static requests, you can do so by setting -Cconfig-E{'Plugin::Static::Simple'}-E{logging}> to 1. +Cconfig-E{static}-E{logging}> to 1. =head2 Forcing directories into static mode @@ -343,7 +342,7 @@ Define a list of top-level directories beneath your 'root' directory that should always be served in static mode. Regular expressions may be specified using C. - MyApp->config->{'Plugin::Static::Simple'}->{dirs} = [ + MyApp->config->{static}->{dirs} = [ 'static', qr/^(images|css)/, ]; @@ -356,19 +355,19 @@ first file found. Note that your root directory is B automatically added to the search path when you specify an C. You should use Cconfig-E{root}> to add it. - MyApp->config->{'Plugin::Static::Simple'}->{include_path} = [ + MyApp->config->{static}->{include_path} = [ '/path/to/overlay', \&incpath_generator, MyApp->config->{root} ]; - + With the above setting, a request for the file C will search for the following files, returning the first one found: /path/to/overlay/images/logo.jpg /dynamic/path/images/logo.jpg /your/app/home/root/images/logo.jpg - + The include path can contain a subroutine reference to dynamically return a list of available directories. This method will receive the C<$c> object as a parameter and should return a reference to a list of directories. Errors can @@ -386,7 +385,7 @@ For example: die "No customer dir defined."; } } - + =head2 Ignoring certain types of files There are some file types you may not wish to serve as static files. @@ -396,9 +395,9 @@ C will be ignored by Static::Simple in the interest of security. If you wish to define your own extensions to ignore, use the C option: - MyApp->config->{'Plugin::Static::Simple'}->{ignore_extensions} + MyApp->config->{static}->{ignore_extensions} = [ qw/html asp php/ ]; - + =head2 Ignoring entire directories To prevent an entire directory from being served statically, you can use @@ -406,8 +405,8 @@ the C option. This option contains a list of relative directory paths to ignore. If using C, the path will be checked against every included path. - MyApp->config->{'Plugin::Static::Simple'}->{ignore_dirs} = [ qw/tmpl css/ ]; - + MyApp->config->{static}->{ignore_dirs} = [ qw/tmpl css/ ]; + For example, if combined with the above C setting, this C value will ignore the following directories if they exist: @@ -416,14 +415,14 @@ C value will ignore the following directories if they exist: /dynamic/path/tmpl /dynamic/path/css /your/app/home/root/tmpl - /your/app/home/root/css + /your/app/home/root/css =head2 Custom MIME types To override or add to the default MIME types set by the L module, you may enter your own extension to MIME type mapping. - MyApp->config->{'Plugin::Static::Simple'}->{mime_types} = { + MyApp->config->{static}->{mime_types} = { jpg => 'image/jpg', png => 'image/png', }; @@ -439,8 +438,8 @@ many compatibility issues with other plugins. Enable additional debugging information printed in the Catalyst log. This is automatically enabled when running Catalyst in -Debug mode. - MyApp->config->{'Plugin::Static::Simple'}->{debug} = 1; - + MyApp->config->{static}->{debug} = 1; + =head1 USING WITH APACHE While Static::Simple will work just fine serving files through Catalyst @@ -479,7 +478,7 @@ files. The final configuration will look something like this: If you are running in a VirtualHost, you can just set the DocumentRoot -location to the location of your root directory; see +location to the location of your root directory; see L. =head1 PUBLIC METHODS @@ -501,7 +500,7 @@ you need to autogenerate them if they don't exist, or they are stored in a mode Static::Simple extends the following steps in the Catalyst process. -=head2 prepare_action +=head2 prepare_action C is used to first check if the request path is a static file. If so, we skip all other C steps to improve @@ -523,7 +522,7 @@ C initializes all default values. =head1 SEE ALSO -L, L, +L, L, L =head1 AUTHOR @@ -538,6 +537,8 @@ Jesse Sheidlower, Guillermo Roditi, +Florian Ragwitz + =head1 THANKS The authors of Catalyst::Plugin::Static: