NAME
    Catalyst::Plugin::Cache::HTTP::Preempt - preemptive HTTP cache control

VERSION
    v0.1.0

REQUIREMENTS
    This module requires Perl v5.10 or later.

    The following non-core Perl modules are required:

    Catalyst
    DateTime
    DateTime::Format::HTTP
    HTTP::Message 6.06
    Moose
    Readonly
    Test::WWW::Mechanize::Catalyst

DESCRIPTION
    This is a Catalyst plugin handles HTTP 1.1 cache-control queries.

    The functionality is similar to Catalyst::Plugin::Cache::HTTP, except
    that it processes the cache control queries before data-intensive
    queries, rather rather than delaying processing until the view is
    generated.

SYNOPSIS
      use Catalyst qw/
        Cache::HTTP::Preempt
      /;

      __PACKAGE__->config(

        'Plugin::Cache::HTTP::Preempt' => {

           no_preempt_head => 0,

           etag_generator => sub {
             my ($c, $config) = @_;
             return "W/" . sprintf("%x", $c->res->headers->last_modified);
           },

        },

      );

CONFIGURATION OPTIONS
    no_preempt_head
        By default, the not_cached method will return `false' for `HEAD'
        requests (even though it will still process cache control headers).

        If you still want to handle `HEAD' requests, then set this option to
        a `true' value.

    no_etag
        Do not set the `ETag' header.

    no_last_modified
        Do not set the `Last-Modified' header.

    no_expires
        Do not set the `Expires' header.

    etag_generator
        You can change how the `ETag' is generated by using the
        `etag_generator' option:

          sub etag_generator {
              my ($c, $config) = @_;
              my $mtime = $c->req->headers->last_modified;
              return sprintf( $config->{strong} ? "%x" : "W/%x" , $mtime);
          }

          if ($c->not_cached({ etag_generator => \&etag_generator }) {
            ...
          }

        This is useful if you want to use something other than the
        modification date of an entity for generating the `ETag'.

        The purpose of this function is to *only* generate the `ETag'. No
        headers should be changed.

        Returning an `undef' value corresponds to not setting an `ETag'.

    strong
        Generate a strong `ETag'. By default, a weak `ETag' is used, since
        the `ETag' is based on the `Last-Modified' time rather than the
        content.

        As per the HTTP 1.1 specification, weak `ETags' will not work with
        the `If-Match' header.

    check_if_range
        When this option, is true, it will check for the `If-Range' and
        `Range' headers. The controller is responsible for sending the
        correct response. (See the discussion for the not_cached method
        below.)

METHODS
  not_cached
      $c->res->headers->last_modified( $obj->mtime );

      ...

      if ($c->not_cached(\%options)) {

         # The response is not cached, so should be generated

         ...

       } else {

         ...

       }

    Checks the requests for HTTP 1.1 cache control headers and handles them
    accordingly.

    This method sets the `ETag' header based on the `Last-Modified' header
    (unless one is already set) and checks for the `If-Modified-Since',
    `If-Unmodified-Since', `If-Match' and `If-None-Match' request headers to
    see if generating the entity can be preempted.

    If the entity can be preempted (i.e. if it has not been modified since
    the given date), then the status is set appropriately, and this method
    returns false.

    Otherwise it returns true. This allows you to avoid reading data from a
    database or otherwise data-intensive processing when it's not actually
    needed.

    If `%options' are given, then they will override the global options.

    If the `Last-Modified' header is unset, then this function will assume
    the last-modification time is the current time.

    If no `Expires' header is set and the function will return a true value,
    then it will set it to the current time. (This is important for web
    browsers that aggressively cache responses, such as Firefox.)

    Cache control options will be processed for `HEAD' requests, but this
    method will always return false, unless the `no_preempt_head' option is
    true.

    If the status is already set to something other than `2xx' when this
    method is called, then the the `If-Match', `If-None-Match' and
    `If-Unmodified-Since' headers will be ignored. However, the `ETag',
    `Expires' and `Last-Modified' headers will still be set. (Ideally, you
    would not be calling the not_cached method if there is an error.)

    If the `check_if_range' option is true, then you *must* check whether
    the status code has been set to `206' or `200', and respond accordingly:

        if ($c->not_cached( { check_if_range => 1 }) {

          if ($c->res->code == 206) {

            # Return partial content as per the Range header.

            ...

          } else {

            # Return full content. Note that the status is set to 200, so
            # it must be updated if there are other errors

            ...

          }

        }

Using with Catalyst::Plugin::Cache::HTTP
    This module can be used with Catalyst::Plugin::Cache::HTTP in a Catalyst
    application, although it is not recommended that you use it in the same
    method.

    If you are using both plugins, then you should modify the view
    processing method to check if an `ETag' header has already been set:

      sub process {
        my $self = shift;
        my $c = $_[0];

        $self->next::method(@_)
            or return 0;

        my $method = $c->req->method;
        return 1
            if ((($method ne 'GET') and ($method ne 'HEAD'))
                or $c->stash->{nocache}); # disable caching explicitely

        unless ($c->res->headers->etag || $c->stash->{no_etag}) {
          ...
        }

      }

Using with Catalyst::Controller::REST
    Catalyst::Controller::REST does not have status helpers for "304 Not
    Modified" and and "412 Precondition Failed" responses.

    To work around this, you need to manually set the entity using an
    undocumented method:

      $c->res->headers->last_modified( $obj->mtime );

      if ($c->modified) {

        # Do more processing to generate the page

      } else {

        $self->_set_entity($c, { error => "Not Modified" });

        return 1;

      }

SEE ALSO
    * Catalyst
    * Catalyst::Plugin::Cache::HTTP
    * HTTP 1.1

AUTHOR
    Interactive Information, Ltd `<cpan at interactive.co.uk>'

SUPPORT
    You can find documentation for this module with the perldoc command.

        perldoc Catalyst::Plugin::Cache::HTTP::Preempt

    You can also find information at:

    * RT: CPAN's request tracker (report bugs here)
        http://rt.cpan.org/NoAuth/Bugs.html?Dist=Catalyst-Plugin-Cache-HTTP-
        Preempt

    * AnnoCPAN: Annotated CPAN documentation
        http://annocpan.org/dist/Catalyst-Plugin-Cache-HTTP-Preempt

    * CPAN Ratings
        http://cpanratings.perl.org/d/Catalyst-Plugin-Cache-HTTP-Preempt

    * Search CPAN
        http://search.cpan.org/dist/Catalyst-Plugin-Cache-HTTP-Preempt/

LICENSE AND COPYRIGHT
    Copyright (c) 2012-2013 Interactive Information, Ltd

    This program is free software; you can redistribute it and/or modify it
    under the terms of the the Artistic License (2.0). You may obtain a copy
    of the full license at:

    http://www.perlfoundation.org/artistic_license_2_0

    Any use, modification, and distribution of the Standard or Modified
    Versions is governed by this Artistic License. By using, modifying or
    distributing the Package, you accept this license. Do not use, modify,
    or distribute the Package, if you do not accept this license.

    If your Modified Version has been derived from a Modified Version made
    by someone other than you, you are nevertheless required to ensure that
    your Modified Version complies with the requirements of this license.

    This license does not grant you the right to use any trademark, service
    mark, tradename, or logo of the Copyright Holder.

    This license includes the non-exclusive, worldwide, free-of-charge
    patent license to make, have made, use, offer to sell, sell, import and
    otherwise transfer the Package with respect to any patent claims
    licensable by the Copyright Holder that are necessarily infringed by the
    Package. If you institute patent litigation (including a cross-claim or
    counterclaim) against any party alleging that the Package constitutes
    direct or contributory patent infringement, then this Artistic License
    to you shall terminate on the date that such litigation is filed.

    Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
    AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
    THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
    PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
    YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
    CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
    CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
    EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

