# Blosxom plugin : conditional_get
# Purpose        : enables conditional GET
# Author         : Ryo Anazawa <anazawa@cpan.org>
# Version        : 0.02000 (2012/05/25)
# Documentation  : See the bottom of this file or type: perldoc conditional_get

package conditional_get;
use strict;
use warnings;

our $VERSION = '0.02000';

sub start { !$blosxom::static_entries }

sub last {
    my ( $h, $env ) = ( $blosxom::header, \%ENV );

    my $method = $env->{REQUEST_METHOD};
    return if $method ne 'GET' and $method ne 'HEAD';

    if ( etag_matches( $h, $env ) or not_modified_since( $h, $env ) ) {

        my @fields = qw/-content_disposition -attachment -content_length/;
        delete @{ $h }{ @fields };

        $h->{-status} = '304 Not Modified';

        # If Content-Type isn't defined,
        # CGI::header() will add the default value.
        # And so makes it defined
        $h->{-type} = q{};

        # Truncate output
        $blosxom::output = q{};
    }

    return;
}

# NOTE: Quoted comments were taken from Plack::Middleware::ConditionalGET
# > RFC 2616 14.25 says it's OK and expected to use 'eq' :)
# >> Note: When handling an If-Modified-Since header field, some
# >> servers will use an exact date comparison function, rather than a
# >> less-than function, for deciding whether to send a 304 ...

sub etag_matches {
    my ( $h, $env ) = @_;
    return if !$h->{-etag} or !$env->{HTTP_IF_NONE_MATCH};
    $h->{-etag} eq _value( $env->{HTTP_IF_NONE_MATCH} );
}

sub not_modified_since {
    my ( $h, $env ) = @_;
    return if !$h->{-last_modified} or !$env->{HTTP_IF_MODIFIED_SINCE};
    $h->{-last_modified} eq _value( $env->{HTTP_IF_MODIFIED_SINCE} );
}

# > IE sends wrong formatted value
# > i.e. "Thu, 03 Dec 2009 01:46:32 GMT; length=17936"
sub _value {
    my $str = shift;
    $str =~ s{;.*$}{};
    $str;
}

1;

__END__

=head1 NAME

Blosxom Plug-in: conditional_get

=head1 VERSION

0.02000 (2012/05/25)

=head1 DESCRIPTION

Enables condtional GET and HEAD using C<If-None-Match> and
C<If-Modified-Since> header.

The application should set either or both of C<Last-Modified>
or C<ETag> response headers according to RFC 2616.
When either of the conditions is met, the response body is set
to be zero length and the status is set to 304 Not Modified.

This plugin is compatible with L<Blosxom::Header>.

=head2 INSTALLATION

'cd' into a directory which contains 'conditional_get' and
'conditional_get.t', and test the plugin:

  $ prove conditional_get.t

Drop the conditional_get plug-in into your Blosxom plugins folder.

=head1 LIMITATIONS

This plugin adds neither C<Last-Modified> nor
C<ETag> response headers. You have to add either or both headers
in your own way. For example,

  package last_modified;
  use strict;
  use warnings;
  use HTTP::Date;

  my @mtime;

  sub start { !$blosxom::static_entries }

  sub date { push @mtime, $_[3] }

  sub last {
      return unless @mtime;
      @mtime = sort { $b <=> $a } @mtime;
      $blosxom::header->{-last_modified} = time2str( $mtime[0] );
  }

  1;

=head1 DEPENDENCIES

L<Blosxom 2.0.0|http://blosxom.sourceforge.org> or higher.

=head1 SEE ALSO

L<Plack::Middleware::ConditionalGET>

=head1 AUTHOR

Ryo Anazawa <anazawa@cpan.org>,
https://github.com/anazawa/

=head1 LICENSE AND COPYRIGHT

Copyright (c) 2012 Ryo Anazawa. All rights reserved.

This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself. See L<perlartistic>.

This program is distributed in the hope that it will we useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE.

=cut
