Completed
Push — master ( c2f224...2bc1cd )
by Oscar
03:33
created

ReadResponse   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 97
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 7

Importance

Changes 15
Bugs 4 Features 2
Metric Value
wmc 14
c 15
b 4
f 2
lcom 0
cbo 7
dl 0
loc 97
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
C __invoke() 0 41 7
B range() 0 22 4
A parseRangeHeader() 0 11 3
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr7Middlewares\Utils;
6
use Psr7Middlewares\Middleware;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
10
/**
11
 * Middleware to read the response.
12
 */
13
class ReadResponse
14
{
15
    use Utils\FileTrait;
16
17
    /**
18
     * Execute the middleware.
19
     *
20
     * @param ServerRequestInterface $request
21
     * @param ResponseInterface      $response
22
     * @param callable               $next
23
     *
24
     * @return ResponseInterface
25
     */
26
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
27
    {
28
        //If basePath does not match
29
        if (!$this->testBasePath($request->getUri()->getPath())) {
30
            return $next($request, $response);
31
        }
32
33
        //If the method is not allowed
34
        if ($request->getMethod() !== 'GET') {
35
            return $response->withStatus(405);
36
        }
37
38
        $file = $this->getFilename($request);
39
40
        //If the file does not exists, check if is gzipped
41
        if (!is_file($file)) {
42
            $file .= '.gz';
43
44
            if (EncodingNegotiator::getEncoding($request) !== 'gzip' || !is_file($file)) {
45
                return $response->withStatus(404);
46
            }
47
48
            $response = $response->withHeader('Content-Encoding', 'gzip');
49
        }
50
51
        $body = Middleware::createStream();
52
53
        $stream = fopen($file, 'r');
54
55
        while (!feof($stream)) {
56
            $body->write(fread($stream, 1024 * 8));
57
        }
58
        fclose($stream);
59
60
        $response = $response->withBody($body);
61
62
        //Handle range header
63
        $response = $this->range($request, $response);
64
65
        return $next($request, $response);
66
    }
67
68
    private static function range(ServerRequestInterface $request, ResponseInterface $response)
69
    {
70
        $response = $response->withHeader('Accept-Ranges', 'bytes');
71
72
        $range = $request->getHeaderLine('Range');
73
74
        if (empty($range) || !($range = self::parseRangeHeader($range))) {
75
            return $response;
76
        }
77
78
        list($first, $last) = $range;
79
        $size = $response->getBody()->getSize();
80
81
        if ($last === null) {
82
            $last = $size - 1;
83
        }
84
85
        return $response
86
            ->withStatus(206)
87
            ->withHeader('Content-Length', (string) ($last - $first + 1))
88
            ->withHeader('Content-Range', sprintf('bytes %d-%d/%d', $first, $last, $size));
89
    }
90
91
    /**
92
     * Parses a range header, for example: bytes=500-999.
93
     *
94
     * @param string $header
95
     *
96
     * @return false|array [first, last]
97
     */
98
    private static function parseRangeHeader($header)
99
    {
100
        if (preg_match('/bytes=(?P<first>\d+)-(?P<last>\d+)?/', $header, $matches)) {
101
            return [
102
                (int) $matches['first'],
103
                isset($matches['last']) ? (int) $matches['last'] : null,
104
            ];
105
        }
106
107
        return false;
108
    }
109
}
110