use strict; use warnings FATAL => 'all'; use Apache::Test; use Apache::TestRequest; use Time::Local; ## mod_expires tests ## ## extra.conf.in: ## ## ## ExpiresActive On ## ExpiresDefault "modification plus 10 years 6 months 2 weeks 3 days 12 hours 30 minutes 19 seconds" ## ExpiresByType text/plain M60 ## ExpiresByType image/gif A120 ## ExpiresByType image/jpeg A86400 ## ## ## calculate "modification plus 10 years 6 months 2 weeks 3 days 12 hours 30 minutes 19 seconds" my $expires_default = calculate_seconds(10,6,2,3,12,30,19); my $htdocs = Apache::Test::vars('documentroot'); my $htaccess = "$htdocs/modules/expires/htaccess/.htaccess"; my @page = qw(index.html text.txt image.gif foo.jpg); my @types = qw(text/plain image/gif image/jpeg); my @directive = qw(ExpiresDefault ExpiresByType); ## first the settings in extra.conf.in (server level) my %exp = default_exp(); my %names = ( 'Date' => 'access', 'Expires' => 'expires', 'Last-Modified' => 'modified', 'Content-Type' => 'type', ); my %month = (); my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); @month{@months} = 0..@months-1; plan tests => (@page * 2) + ((((@page * 3) * @types) + @page) * 2) + @page, have_module 'expires'; foreach my $page (@page) { my $head = HEAD_STR "/modules/expires/$page"; $head = '' unless defined $head; print "# debug: $page\n$head\n"; ok ($head =~ /^HTTP\/1\.[1|0] 200 OK/); ok expires_test(1,$head); } unlink $htaccess if -e $htaccess; ## with no .htaccess file, everything should be inherited here ## foreach my $page (@page) { my $head = HEAD_STR "/modules/expires/htaccess/$page"; ok expires_test(1,$head); } ## testing with .htaccess ## foreach my $on_off (qw(On Off)) { my $ExpiresActive = "ExpiresActive $on_off\n"; write_htaccess($ExpiresActive); %exp = default_exp(); ## if ExpiresActive is 'On', everything else will be inherited ## foreach my $page (@page) { my $head = HEAD_STR "/modules/expires/htaccess/$page"; print "# ---\n# $ExpiresActive"; ok expires_test(($on_off eq 'On'),$head); } foreach my $t (@types) { my ($head, $directive_string, $gmsec, $a_m, $ExpiresDefault, $ExpiresByType); ## testing with just ExpiresDefault directive ## $a_m = (qw(A M))[int(rand(2))]; ($gmsec, $ExpiresDefault) = get_rand_time_str($a_m); %exp = default_exp(); set_exp('default', "$a_m$gmsec"); $directive_string = $ExpiresActive . "ExpiresDefault $ExpiresDefault\n"; write_htaccess($directive_string); foreach my $page (@page) { $head = HEAD_STR "/modules/expires/htaccess/$page"; print "#---\n# $directive_string"; ok expires_test(($on_off eq 'On'), $head); } ## just ExpiresByType directive ## $a_m = (qw(A M))[int(rand(2))]; ($gmsec, $ExpiresByType) = get_rand_time_str($a_m); %exp = default_exp(); set_exp($t, "$a_m$gmsec"); $directive_string = $ExpiresActive . "ExpiresByType $t $ExpiresByType\n"; write_htaccess($directive_string); foreach my $page (@page) { $head = HEAD_STR "/modules/expires/htaccess/$page"; print "# ---\n# $directive_string"; ok expires_test(($on_off eq 'On'), $head); } ## both ## $a_m = (qw(A M))[int(rand(2))]; ($gmsec, $ExpiresDefault) = get_rand_time_str($a_m); %exp = default_exp(); set_exp('default', "$a_m$gmsec"); $a_m = (qw(A M))[int(rand(2))]; ($gmsec, $ExpiresByType) = get_rand_time_str($a_m); set_exp($t, "$a_m$gmsec"); $directive_string = $ExpiresActive . "ExpiresDefault $ExpiresDefault\n" . "ExpiresByType $t $ExpiresByType\n"; write_htaccess($directive_string); foreach my $page (@page) { $head = HEAD_STR "/modules/expires/htaccess/$page"; print "# ---\n# $directive_string"; ok expires_test(($on_off eq 'On'), $head); } } } ## clean up ## unlink $htaccess if -e $htaccess; sub set_exp { my $key = shift; my $exp = shift; if ($key eq 'all') { foreach (keys %exp) { $exp{$_} = $exp; } } else { $exp{$key} = $exp; } } sub get_rand_time_str { my $a_m = shift; my ($y, $m, $w, $d, $h, $mi, $s, $rand_time_str); $y = int(rand(2)); $m = int(rand(4)); $w = int(rand(3)); $d = int(rand(20)); $h = int(rand(9)); $mi = int(rand(50)); $s = int(rand(50)); my $gmsec = calculate_seconds($y,$m,$w,$d,$h,$mi,$s); ## whether to write it out or not ## if (int(rand(2))) { ## write it out ## ## access or modification ## if ($a_m eq 'A') { $rand_time_str = "\"access plus"; } else { $rand_time_str = "\"modification plus"; } $rand_time_str .= " $y years" if $y; $rand_time_str .= " $m months" if $m; $rand_time_str .= " $w weeks" if $w; $rand_time_str .= " $d days" if $d; $rand_time_str .= " $h hours" if $h; $rand_time_str .= " $mi minutes" if $mi; $rand_time_str .= " $s seconds" if $s; $rand_time_str .= "\""; } else { ## easy format ## $rand_time_str = "$a_m$gmsec"; } return ($gmsec, $rand_time_str); } sub write_htaccess { open (HT, ">$htaccess") or die "cant open $htaccess: $!"; print HT shift; close(HT); } sub expires_test { my $expires_active = shift; my $head_str = shift; my %headers = (); foreach my $header (split /\n/, $head_str) { if ($header =~ /^([\-\w]+): (.*)$/) { print "# debug: [$1] [$2]\n"; $headers{$names{$1}} = $2 if exists $names{$1}; } } ## expires header should not exist if ExpiresActive is Off ## return !$headers{expires} unless ($expires_active); for my $h (grep !/^type$/, values %names) { print "# debug: $h @{[$headers{$h}||'']}\n"; if ($headers{$h}) { $headers{$h} = convert_to_time($headers{$h}) || 0; } else { $headers{$h} = 0; } print "# debug: $h $headers{$h}\n"; } my $exp_conf = ''; if ( exists $exp{ $headers{type} } and $exp{ $headers{type} }) { $exp_conf = $exp{ $headers{type} }; } else { $exp_conf = $exp{'default'}; } ## if expect is set to '0', Expire header should not exist. ## if ($exp_conf eq '0') { return !$headers{expires}; } my $expected = ''; my $exp_type = ''; if ($exp_conf =~ /^([A|M])(\d+)$/) { $exp_type = $1; $expected = $2; ## With modification date as base expire times can be in the past ## Correct behaviour for the server in this case is to set expires ## time equal to access time. if (($exp_type eq 'M') && ($headers{access} > $headers{modified} + $expected)) { $expected = $headers{access} - $headers{modified}; } } else { print STDERR "\n\ndoom: $exp_conf\n\n"; return 0; } my $actual = 0; if ($exp_type eq 'M') { $actual = $headers{expires} - $headers{modified}; } elsif ($exp_type eq 'A') { $actual = $headers{expires} - $headers{access}; } print "# debug: expected: $expected\n"; print "# debug: actual : $actual\n"; return ($actual == $expected); } sub convert_to_time { my $timestr = shift; return undef unless $timestr; my ($sec,$min,$hours,$mday,$mon,$year); if ($timestr =~ /^\w{3}, (\d+) (\w{3}) (\d{4}) (\d{2}):(\d{2}):(\d{2}).*$/) { $mday = $1; $mon = $month{$2}; $year = $3; $hours = $4; $min = $5; $sec = $6; } return undef unless defined $sec && defined $min && defined $hours && defined $mday && defined $mon && defined $year; return Time::Local::timegm($sec, $min, $hours, $mday, $mon, $year); } sub calculate_seconds { ## takes arguments: ## years, months, weeks, days, hours, minutes, seconds my $exp_years = shift() * 60 * 60 * 24 * 365; my $exp_months = shift() * 60 * 60 * 24 * 30; my $exp_weeks = shift() * 60 * 60 * 24 * 7; my $exp_days = shift() * 60 * 60 * 24; my $exp_hours = shift() * 60 * 60; my $exp_minutes = shift() * 60; return $exp_years + $exp_months + $exp_weeks + $exp_days + $exp_hours + $exp_minutes + shift; } sub default_exp { ## set the exp hash to the defaults as defined in the conf file. return ( 'default' => "M$expires_default", 'text/plain' => 'M60', 'image/gif' => 'A120', 'image/jpeg' => 'A86400' ); }