Mojo-UserAgent-Cached

 view release on metacpan or  search on metacpan

lib/Mojo/UserAgent/Cached.pm  view on Meta::CPAN

    expires_on_backend => $ENV{MUAC_CACHE_EXPIRES_ON_BACKEND} // 1,
    max_key_length     => 140,
    %{ shift->cache_opts || {} },
  )
};
has 'cache_opts'                 => sub { {} };
has 'cache_url_opts'             => sub { {} };
has 'key_generator'              => sub { \&key_generator_cb; };
has 'logger'                     => sub { Mojo::Log->new() };
has 'access_log'                 => sub { $ENV{MUAC_ACCESS_LOG} || '' };
has 'use_expired_cached_content' => sub { $ENV{MUAC_USE_EXPIRED_CACHED_CONTENT} // 1 };
has 'accepted_error_codes'       => sub { $ENV{MUAC_ACCEPTED_ERROR_CODES} || '' };
has 'sorted_queries'             => 1;

has 'created_stacktrace' => '';

sub new {
    my ($class, %opts) = @_;

    my %mojo_agent_config = map { $_ => $opts{$_} } grep { exists $opts{$_} } qw/
        ca
        cert
        connect_timeout
        cookie_jar
        inactivity_timeout
        insecure
        ioloop
        key
        local_address
        max_connections
        max_redirects
        max_response_size
        proxy
        request_timeout
        server
        transactor
    /;

    my $ua = $class->SUPER::new(%mojo_agent_config);

    # Populate attributes
    map { $ua->$_( $opts{$_} ) } grep { exists $opts{$_} } qw/
        local_dir
        always_return_file
        cache_opts
        cache_agent
        cache_url_opts
        logger
        access_log
        use_expired_cached_content
        accepted_error_codes
        sorted_queries
    /;

    $ua->created_stacktrace($ua->_get_stacktrace);

    return bless($ua, $class);
}


sub invalidate {
    my ($self, $key) = @_;

    if ($self->is_cacheable($key)) {
        $self->logger->debug("Invalidating cache for '$key'");
        return $self->cache_agent->remove($key);
    }

    return;
}

sub expire {
    my ($self, $key) = @_;

    if ($self->is_cacheable($key)) {
        $self->logger->debug("Expiring cache for '$key'");
        return $self->cache_agent->expire($key);
    }

    return;
}

sub build_tx {
  my ($self, $method, $url, @more) = @_;

  $url = ($self->always_return_file || $url);

  if ($url !~ m{^(/|[^/]+:)}) {
    if ($self->local_dir) {
      $url = 'file://' . File::Spec->catfile($self->local_dir, "$url");
    } elsif ($self->always_return_file) {
      $url = 'file://' . "$url";
    } elsif ($url !~ m{^(/|[^/]+:)}) {
      $url = 'file://' . Cwd::realpath("$url");
    }
  }

  $self->transactor->tx($method, $url, @more);
}

sub start {
  my ($self, $tx, $cb) = @_;

  my $url     = $tx->req->url->to_unsafe_string;
  my $method  = $tx->req->method;
  my $headers = $tx->req->headers->to_hash(1);
  my $content = $tx->req->content->asset->slurp;
  delete $headers->{'User-Agent'};
  delete $headers->{'Accept-Encoding'};
  my @opts = (($method eq 'GET' ? () : $method), (keys %{ $headers || {} } ? $headers : ()), $content || ());
  my $key = $self->generate_key($url, @opts);
  my $start_time = time;

  # Fork-safety
  $self->_cleanup->server->restart if $self->{pid} && $self->{pid} ne $$;
  $self->{pid} //= $$;

  # We wrap the incoming callback in our own callback to be able to cache the response
  my $wrapper_cb = $cb ? sub {
    my ($ua, $tx) = @_;
    $cb->($ua, $ua->_post_process_get($tx, $start_time, $key, @opts));

lib/Mojo/UserAgent/Cached.pm  view on Meta::CPAN

  # Example:
  # Returning fetched 'https://graph.facebook.com?ids=http%3A%2F%2Fexample.com%2Flivet%2F20...-lommebok&access_token=1234' => 200 for A.C.Facebook:133,185,183,A.M.F.ArticleList:19,9,A.M.Selector:47,responsive/modules/most-shared.html.tt:15,15,13,temp...

Format:
  Returning <cache-status> '<URL>' => 'HTTP code' for <request_stacktrace> (<created_stacktrace>)

  cache-status: (cached|fetched|cached+expired)
  URL: the URL requested, shortened when it is really long
  request_stacktrace: Simplified stacktrace with leading module names shortened, also includes TT stacktrace support. Line numbers in the same module are grouped (order kept of course).
  created_stacktrace: Stack trace for creation of UA object, useful to see what options went in, and which object is used. Same format as normal stacktrace, but skips common parts.
  
  Example:
    created_stacktrace: A.C.Facebook:68,E.C.Sandbox_874:7,A.C.Facebook:133,<common part replaced>,main:14
    stacktrace: A.C.Facebook:133,< common part: 185,183,A.M.F.ArticleList:19,9,A.M.Selector:47,responsive/modules/most-shared.html.tt:15,15,13,templates/inc/macros.tt:125,138,templates/responsive/frontpage.html.tt:10,10,16,Template:66,A.G.C.Article:3...

=head2 access_log

A file that will get logs of every request, the format is a hybrid of Apache combined log, including time spent for the request.
If provided the file will be written to. Defaults to C<$ENV{MUAC_ACCESS_LOG} || ''> which means no log will be written.

=head2 use_expired_cached_content

Indicates that we will send expired, cached content back. This means that if a request fails, and the cache has expired, you
will get back the last successful content. Defaults to C<$ENV{MUAC_EXPIRED_CONTENT} // 1>

=head2 accepted_error_codes

A list of error codes that should not be considered as errors. For instance this means that the client will not look for expired
cached content for requests that result in this response. Defaults to C<$ENV{MUAC_ACCEPTED_ERROR_CODES} || ''>

=head2 sorted_queries

Setting this to a true value will sort query parameters in the resulting URL. This means that requests will be identical if the key/value pairs
are the same. This helps when URLs have been built up using hashes that may have random orders.

=head1 OVERRIDEN ATTRIBUTES

In addition L<Mojo::UserAgent::Cached> overrides the following L<Mojo::UserAgent> attributes.

=head2 connect_timeout

Defaults to C<$ENV{MOJO_CONNECT_TIMEOUT} // 2>

=head2 inactivity_timeout

Defaults to C<$ENV{MOJO_INACTIVITY_TIMEOUT} // 5>

=head2 max_redirects

Defaults to C<$ENV{MOJO_MAX_REDIRECTS} // 4>

=head2 request_timeout

Defaults to C<$ENV{MOJO_REQUEST_TIMEOUT} // 10>

=head1 METHODS

L<Mojo::UserAgent::Cached> inherits all methods from L<Mojo::UserAgent> and
implements the following new ones.

=head2 invalidate

  $ua->invalidate($key);

Deletes the cache of the given $key.

=head2 expire

  $ua->expire($key);

Set the cache of the given $key as expired.

=head2 set

  my $tx = $ua->build_tx(GET => "http://localhost:$port", ...);
  $tx = $ua->start($tx);
  my $cache_key = $ua->generate_key("http://localhost:$port", ...);
  $ua->set($cache_key, $tx);

Set allows setting data directly for a given URL

=head2 generate_key(@params)

Returns a key to be used for the cache agent. It accepts the same parameters
that a normal ->get() request does.

=head2 validate_key

  my $status = $ua4->validate_key('http://example.com');

Fast validates if key is valid in cache without doing fetch.
Return 1 if true.

=head2 sort_query($url)

Returns a string with the URL passed, with sorted query parameters suitable for cache lookup

=head1 OVERRIDEN METHODS

=head2 new

  my $ua = Mojo::UserAgent::Cached->new( request_timeout => 1, ... );

Accepts the attributes listed above and all attributes from L<Mojo::UserAgent>.
Stores its own attributes and passes on the relevant ones when creating a
parent L<Mojo::UserAgent> object that it inherits from. Returns a L<Mojo::UserAgent::Cached> object

=head2 get(@params)

  my $tx = $ua->get('http://example.com');

Accepts the same arguments and returns the same as L<Mojo::UserAgent>.

It will try to return a cached version of the $url, adhering to the set or default attributes.

In addition if a relative file path is given, it tries to return the file appended to
the attribute C<local_dir>. In this case a fake L<Mojo::Transaction::HTTP> object is returned,
populated with a L<Mojo::Message::Request> with method and url, and a L<Mojo::Message::Response>
with headers, code and body set.

=head1 ENVIRONMENT VARIABLES

C<$ENV{MUAC_CLIENT_WRITE_LOCAL_FILE_RES_DIR}> can be set to a directory to store a request in:

  # Re-usable local file with headers and metadata ends up at 't/data/dir/lol/foo.html?bar=1'
  $ENV{MUAC_CLIENT_WRITE_LOCAL_FILE_RES_DIR}='t/data/dir';
  Mojo::UserAgent::Cached->new->get("http://foo.com/lol/foo.html?bar=1");

=head1 SEE ALSO

L<Mojo::UserAgent>, L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.

=head1 COPYRIGHT

Nicolas Mendoza (2015-), ABC Startsiden (2015)

=head1 LICENSE

Same as Perl licence as per agreement with ABC Startsiden on 2015-06-02

=cut



( run in 0.642 second using v1.01-cache-2.11-cpan-2398b32b56e )