Completed
Push — master ( 2ae6ae...28d798 )
by Paweł
22s queued 12s
created

FileDownloader::retryDecider()   B

Complexity

Conditions 7
Paths 1

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 38
rs 8.3786
c 0
b 0
f 0
cc 7
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Superdesk Web Publisher Content Bundle.
7
 *
8
 * Copyright 2019 Sourcefabric z.ú. and contributors.
9
 *
10
 * For the full copyright and license information, please see the
11
 * AUTHORS and LICENSE files distributed with this source code.
12
 *
13
 * @copyright 2019 Sourcefabric z.ú
14
 * @license http://www.superdesk.org/license
15
 */
16
17
namespace SWP\Bundle\ContentBundle\File;
18
19
use function rtrim;
20
use function sha1;
21
use function strlen;
22
use function sys_get_temp_dir;
23
use function pathinfo;
24
use GuzzleHttp\Client;
25
use GuzzleHttp\Exception\ConnectException;
26
use GuzzleHttp\Exception\RequestException;
27
use GuzzleHttp\Handler\CurlHandler;
28
use GuzzleHttp\HandlerStack;
29
use GuzzleHttp\Middleware;
30
use GuzzleHttp\Psr7\Request;
31
use GuzzleHttp\Psr7\Response;
32
use Hoa\Mime\Mime;
33
use Psr\Log\LoggerInterface;
34
use Symfony\Component\HttpFoundation\File\UploadedFile;
35
36
final class FileDownloader implements FileDownloaderInterface
37
{
38
    private $logger;
39
40
    private $retryDownloads;
41
42
    public function __construct(LoggerInterface $logger, bool $retryDownloads)
43
    {
44
        $this->logger = $logger;
45
        $this->retryDownloads = $retryDownloads;
46
    }
47
48
    public function download(string $url, string $mediaId, string $mimeType = null): UploadedFile
49
    {
50
        $pathParts = pathinfo($url);
51
        if (null === $mimeType) {
52
            $mimeType = Mime::getMimeFromExtension($pathParts['extension']);
53
        }
54
55
        $handlerStack = HandlerStack::create(new CurlHandler());
56
        $handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));
57
        $client = new Client(array('handler' => $handlerStack));
58
        $tempLocation = rtrim(sys_get_temp_dir(), '/').DIRECTORY_SEPARATOR.sha1($mediaId.date('his'));
59
        $client->request('GET', $url, ['sink' => $tempLocation]);
60
61
        return new UploadedFile($tempLocation, $mediaId, $mimeType, strlen($tempLocation), null, true);
62
    }
63
64
    private function retryDecider(): callable
65
    {
66
        return function (
67
            $retries,
68
            Request $request,
69
            Response $response = null,
70
            RequestException $exception = null
71
        ): bool {
72
            $retry = false;
73
            if (!$this->retryDownloads) {
74
                $this->logger->error(\sprintf('Retries are disabled'));
75
76
                return false;
77
            }
78
79
            if ($retries >= 4) {
80
                $this->logger->error(\sprintf('Maximum number of retires reached'));
81
82
                return false;
83
            }
84
85
            // Retry connection exceptions
86
            if ($exception instanceof ConnectException) {
87
                $retry = true;
88
            }
89
90
            // Retry on server errors
91
            if ($response && $response->getStatusCode() >= 400) {
92
                $retry = true;
93
            }
94
95
            if (true === $retry) {
96
                $this->logger->info(\sprintf('Retry downloading %s', $request->getUri()));
97
            }
98
99
            return $retry;
100
        };
101
    }
102
103
    private function retryDelay(): callable
104
    {
105
        return static function ($numberOfRetries): int {
106
            return 1000 * $numberOfRetries;
107
        };
108
    }
109
}
110