From 53986711c7833878ecc3c43478712023619e1ffc Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 27 Jan 2023 15:11:16 +0100 Subject: [PATCH] Add support for multiple download attempts for assets that need to be fetched This will be disabled by default, and can be enabled with `$app->asset->store->retries(5)`. --- lib/Mojolicious/Plugin/AssetPack/Store.pm | 31 +++++++++++++-- t/fetch-retry.t | 47 +++++++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 t/fetch-retry.t diff --git a/lib/Mojolicious/Plugin/AssetPack/Store.pm b/lib/Mojolicious/Plugin/AssetPack/Store.pm index 7e99060..f591589 100644 --- a/lib/Mojolicious/Plugin/AssetPack/Store.pm +++ b/lib/Mojolicious/Plugin/AssetPack/Store.pm @@ -8,6 +8,7 @@ use Mojo::URL; use Mojolicious::Types; use Mojolicious::Plugin::AssetPack::Asset; use Mojolicious::Plugin::AssetPack::Util qw(diag checksum has_ro DEBUG); +use Time::HiRes qw(sleep); use constant CACHE_DIR => 'cache'; @@ -26,6 +27,8 @@ has asset_class => 'Mojolicious::Plugin::AssetPack::Asset'; has default_headers => sub { +{"Cache-Control" => "max-age=31536000"} }; has fallback_headers => sub { +{"Cache-Control" => "max-age=60"} }; has fallback_templates => sub { +{%FALLBACK_TEMPLATES} }; +has retry_delay => 3; +has retries => 0; has _types => sub { my $t = Mojolicious::Types->new; @@ -257,14 +260,22 @@ sub _download { return $asset if $attrs{url}->host ne 'local' and $asset = $self->_already_downloaded($url); - my $tx = $self->ua->get($url); - my $h = $tx->res->headers; + my $tx; + my $retries = $self->retries; + while (1) { + $tx = $self->ua->get($url); + last unless my $err = $tx->error; + + if ($retries-- > 0) { + sleep $self->retry_delay; + next; + } - if (my $err = $tx->error) { $self->_log->warn("[AssetPack] Unable to download $url: $err->{message}"); return undef; } + my $h = $tx->res->headers; my $ct = $h->content_type || ''; if ($ct ne 'text/plain') { $ct =~ s!;.*$!!; @@ -388,6 +399,20 @@ This is currently an EXPERIMENTAL feature. See L for details. +=head2 retry_delay + + my $delay = $self->retry_delay; + $self = $self->retry_delay(0.5); + +Delay in seconds between download attempts for assets that need to be fetched, defaults to C<3>. + +=head2 retries + + my $retries = $self->retries; + $self = $self->retries(5); + +Number of times asset downloads will be attempted for assets that need to be fetched, defaults to C<0>. + =head2 ua $ua = $self->ua; diff --git a/t/fetch-retry.t b/t/fetch-retry.t new file mode 100644 index 0000000..aaf1d1c --- /dev/null +++ b/t/fetch-retry.t @@ -0,0 +1,47 @@ +use lib '.'; +use t::Helper; + +use Mojo::IOLoop; +use Mojo::Server::Daemon; +use Mojolicious; + +my $t = t::Helper->t(pipes => [qw(Css Fetch)]); + +my $app = Mojolicious->new; +$app->config->{attempts} = 0; +$app->routes->get( + '/test.css' => sub { + my $c = shift; + return $c->render(data => 'Internal server error', status => 500) if $c->app->config->{attempts}++ <= 2; + $c->render(data => 'body { color: #00f }'); + } +); +my $responses = []; +$app->hook( + before_dispatch => sub { + shift->on(finish => sub { push @$responses, shift->res->code }); + } +); +my $daemon = Mojo::Server::Daemon->new(listen => ['http://*'], ioloop => $t->app->asset->ua->ioloop, app => $app); +my $port = $daemon->start->ports->[0]; + +subtest 'Defaults' => sub { + is $t->app->asset->store->retries, 0, 'no retries by default'; + is $t->app->asset->store->retry_delay, 3, '3 second retry delay by default'; +}; + +subtest 'Download asset with multiple attempts' => sub { + $t->app->asset->store->retries(3)->retry_delay(0.1); + $t->app->asset->process('app.css' => "http://127.0.0.1:$port/test.css"); + + $t->get_ok('/')->status_is(200)->content_like(qr{Hello world}); + $t->get_ok($t->tx->res->dom->at('link')->{href})->status_is(200)->content_like(qr{body.+color.+00f}); + is_deeply $responses, [500, 500, 500, 200], 'right responses'; +}; + +done_testing; + +__DATA__ +@@ index.html.ep +%= asset 'app.css' +Hello world