Subject: | Several improvements - with patch |
Performance can still be improved by better caching. This patch
implements caching for index and photo frame pages.
It seems to work reasonably well, although Internet Explorer often
doesn't seem to do conditional GETs when I think it should.
Perhaps you can figure out why. Even so, this patch serves these pages
from the cache insted of building them on the fly. It's much more
responsive. (Server logfiles confirm this.)
Cache poisoning attacks were also a problem, as the previous code used
world writable permissions for the cache structure. This patch uses
more reasonable defaults -- and allows customization options.
For a more persistent store, you can now move the cache root
from /tmp. You can also de-couple its namespace from the gallery title.
Finally, failure to open the captions file should now get a 'file not
found' rather than 'internal error page'.
Hopefully, others will find this useful.
--- /home/litt/tmp/CGI-Application-PhotoGallery-
0.13/lib/CGI/Application/PhotoGallery.pm 2008-03-10
21:25:23.000000000 -0400
+++ /usr/lib/perl5/site_perl/5.8.8/CGI/Application/PhotoGallery.pm
2008-03-11 11:23:32.000000000 -0400
@@ -138,6 +138,29 @@
and height attributes on the image tag, thus saving the image will
retain the
full resolution.
+=head2 cache_root
+
+Specifies where the file cache data will be stored. Defaults to
FileCache
+under the OS-specific temporary filesdirectory (e.g. /tmp/FileCache).
You
+may want to move this to make the cache persist. Under selinux,
however,
+be careful to put it in a webserver-writable directory.
+
+=head2 cache_namespace
+
+Specifies the namespace for this gallery's cache data. Defaults to the
+gallery title - or 'default'. Changing this will orphan the cache
data.
+
+=head2 cache_dirumask
+
+Specifies the umask value to use when cache directories are created.
Defaults
+to 0007 to avoid cache poisioning attacks.
+
+=head2 cache_datumask
+
+Specifies the umask value to use when cache data is written. Defaults
to 006
+to avoid cache poisioning attacks. Note that this becomes the umask
for all
+files written by this script. (See Cache::FileCache documentation for
why.)
+
=head1 CAPTIONS
You can include captions for your photos by creating a tab-separated
@@ -283,8 +306,17 @@
my $self = shift;
unless ( $self->{ _cache } ) {
- $self->{ _cache } = Cache::FileCache->new(
- { namespace => $self->param( 'title' ) } );
+ my %options = ( namespace => $self->param( 'title' ),
directory_umask => 0007 );
+
+ $options{'cache_root'} = $self->param( 'cache_root' ) if
defined $self->param( 'cache_root' );
+ $options{'namespace'} = $self->param( 'cache_namespace' ) if
defined $self->param( 'cache_namespace' );
+ $options{'directory_umask'} = $self->param( 'cache_dirumask' )
if defined $self->param( 'cache_dirumask' );
+ if( defined $self->param( 'cache_datumask' ) ) {
+ umask $self->param( 'cache_datumask' );
+ } else {
+ umask 006;
+ }
+ $self->{ _cache } = Cache::FileCache->new( \%options );
}
return $self->{ _cache };
@@ -302,6 +334,7 @@
sub gallery_index {
my $self = shift;
my $types = $self->mime_types;
+ my $query = $self->query;
my $limit = $self->param( 'preview_thumbs' );
my $photo_dir = $self->param( 'photos_dir' );
@@ -317,6 +350,36 @@
die "ERROR: File not found." unless -e $directory;
die "ERROR: '$directory' is not a directory" unless -d $directory;
+ my $output;
+ my $cache = $self->cache;
+ my $key = $directory;
+ my $lastmod = ( stat( $directory ) )[ 9 ];
+ my $cstamp = "$directory/.cachetime";
+
+ if ( $output = $cache->get( $key ) ) {
+ my $cachetime = $cache->get( $cstamp );
+ if( $cachetime && $cachetime == $lastmod ) {
+ my $reqmod;
+ if ( my $header = $query->http( 'If-Modified-Since' ) ) {
+ $reqmod
+ = HTTP::Date::str2time( ( split( /;/, $header, 2 ) )
[ 0 ] );
+
+ if ( $reqmod && $reqmod == $lastmod ) {
+ $self->header_props( { -status => '304 Not
Modified' } );
+ return;
+ }
+ }
+
+ $self->header_add(
+ {
+#Debug -status => "200 Cached",
+ -last_modified => HTTP::Date::time2str( $lastmod )
+ }
+ );
+ return $output
+ }
+ }
+
my @dirs = sort File::Find::Rule->directory->mindepth( 1 )-
Show quoted text
>maxdepth( 1 )
->in( $directory );
@@ -358,7 +421,16 @@
parent => $parent,
);
- return $html->output;
+ $self->header_add(
+ {
+ -last_modified => HTTP::Date::time2str( $lastmod )
+ }
+ );
+ $output = $html->output;
+ $cache->set( $key => $output );
+ $cache->set( $cstamp => $lastmod );
+
+ return $output;
}
=head2 thumbnail( )
@@ -472,6 +544,45 @@
die 'ERROR: Missing photo query argument.' unless $photo;
die 'ERROR: File not found' unless -e $path;
+ my $caption_path = "$dir/captions.txt";
+
+ my $output;
+ my $cache = $self->cache;
+ my $key = "$path.#frame";
+ my $lastmod = ( stat( $path ) )[ 9 ];
+ # Directory change means links may have changed
+ # Caption file change is a content change
+ my $lastdir = ( stat( $dir ) )[ 9 ];
+ $lastmod = $lastdir if( $lastdir > $lastmod );
+ my $lastcap = 0;
+ $lastcap = ( stat( $caption_path ) )[ 9 ] if( -r $caption_path );
+ $lastmod = $lastcap if( $lastcap > $lastmod );
+ my $cstamp = "$key#cachetime";
+
+ if ( $output = $cache->get( $key ) ) {
+ my $cachetime = $cache->get( $cstamp );
+ if( $cachetime && $cachetime == $lastmod ) {
+ my $reqmod;
+ if ( my $header = $query->http( 'If-Modified-Since' ) ) {
+ $reqmod
+ = HTTP::Date::str2time( ( split( /;/, $header, 2 ) )
[ 0 ] );
+
+ if ( $reqmod && $reqmod == $lastmod ) {
+ $self->header_props( { -status => '304 Not
Modified' } );
+ return;
+ }
+ }
+
+ $self->header_add(
+ {
+#debug -status => "200 Cached",
+ -last_modified => HTTP::Date::time2str( $lastmod )
+ }
+ );
+ return $output
+ }
+ }
+
my $gfx = $self->gfx_lib;
my ( $width, $height ) = $gfx->size( $path );
@@ -521,10 +632,9 @@
);
# get caption, if available
- my $caption_path = "$dir/captions.txt";
if ( -e $caption_path ) {
open( CAPTIONS, $caption_path )
- or die "ERROR: Cannot open $caption_path: $!";
+ or die "ERROR: caption file not found $caption_path: $!";
while ( my $caption = <CAPTIONS> ) {
if ( $caption =~ /^\Q$photo\E\t(.+)$/ ) {
$html->param( caption => $1 );
@@ -534,7 +644,16 @@
close( CAPTIONS );
}
- return $html->output;
+ $self->header_add(
+ {
+ -last_modified => HTTP::Date::time2str( $lastmod )
+ }
+ );
+ $output = $html->output;
+ $cache->set( $key => $output );
+ $cache->set( $cstamp => $lastmod );
+
+ return $output;
}
sub _dist_file {