ResourceServlet   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 124
ccs 0
cts 61
cp 0
rs 10
c 0
b 0
f 0
wmc 13

7 Methods

Rating   Name   Duplication   Size   Complexity  
A sendResourceRanges() 0 19 2
A sendCompleteResource() 0 9 1
A generateDefaultHeaders() 0 13 3
A __construct() 0 3 1
A sendHeaders() 0 8 3
A getContentRangeHeader() 0 3 1
A sendResource() 0 9 2
1
<?php declare(strict_types=1);
2
3
namespace DaveRandom\Resume;
4
5
final class ResourceServlet
6
{
7
    /**
8
     * @var \DaveRandom\Resume\Resource
9
     */
10
    private $resource;
11
12
    /**
13
     * Generate the default response headers for this resource
14
     *
15
     * @return HeaderSet
16
     */
17
    private function generateDefaultHeaders(): HeaderSet
18
    {
19
        $ranges = $this->resource instanceof RangeUnitProvider
20
            ? \implode(',', $this->resource->getRangeUnits())
21
            : 'bytes';
22
23
        if ($ranges === '') {
24
            $ranges = 'none';
25
        }
26
27
        return new HeaderSet([
28
            'Content-Type' => $this->resource->getMimeType(),
29
            'Accept-Ranges' => $ranges,
30
        ]);
31
    }
32
33
    /**
34
     * Send the headers that are included regardless of whether a range was requested
35
     *
36
     * @param OutputWriter $outputWriter
37
     * @param HeaderSet $headers
38
     */
39
    private function sendHeaders(OutputWriter $outputWriter, HeaderSet $headers)
40
    {
41
        foreach ($this->resource->getAdditionalHeaders() as $name => $value) {
42
            $headers->setHeader($name, $value);
43
        }
44
45
        foreach ($headers as $name => $value) {
46
            $outputWriter->sendHeader(\trim($name), \trim($value));
47
        }
48
    }
49
50
    /**
51
     * Create a Content-Range header corresponding to the specified unit and ranges
52
     *
53
     * @param string $unit
54
     * @param Range[] $ranges
55
     * @param int $size
56
     * @return string
57
     */
58
    private function getContentRangeHeader(string $unit, array $ranges, int $size): string
59
    {
60
        return $unit . ' ' . \implode(',', $ranges) . '/' . $size;
61
    }
62
63
    /**
64
     * Send the complete resource to the client
65
     *
66
     * @param OutputWriter $outputWriter
67
     * @param HeaderSet $headers
68
     */
69
    private function sendCompleteResource(OutputWriter $outputWriter, HeaderSet $headers)
70
    {
71
        $outputWriter->setResponseCode(200);
72
73
        $this->sendHeaders($outputWriter, $headers);
74
75
        $headers->setHeader('Content-Length', (string)$this->resource->getLength());
76
77
        $this->resource->sendData($outputWriter);
78
    }
79
80
    /**
81
     * Send the requested ranges to the client
82
     *
83
     * @param OutputWriter $outputWriter
84
     * @param HeaderSet $headers
85
     * @param RangeSet $rangeSet
86
     */
87
    private function sendResourceRanges(OutputWriter $outputWriter, HeaderSet $headers, RangeSet $rangeSet)
88
    {
89
        $totalResourceSize = $this->resource->getLength();
90
        $ranges = $rangeSet->getRangesForSize($totalResourceSize);
91
92
        $responseBodySize = \array_reduce($ranges, function(int $size, Range $range) {
93
            return $size + $range->getLength();
94
        }, 0);
95
96
        $outputWriter->setResponseCode(206);
97
        $this->sendHeaders($outputWriter, $headers);
98
99
        $contentRangeHeader = $this->getContentRangeHeader($rangeSet->getUnit(), $ranges, $totalResourceSize);
100
101
        $outputWriter->sendHeader('Content-Range', $contentRangeHeader);
102
        $outputWriter->sendHeader('Content-Length', (string)$responseBodySize);
103
104
        foreach ($ranges as $range) {
105
            $this->resource->sendData($outputWriter, $range);
106
        }
107
    }
108
109
    public function __construct(Resource $resource)
110
    {
111
        $this->resource = $resource;
112
    }
113
114
    /**
115
     * Send data from a file based on the Range header described by the supplied RangeSet
116
     *
117
     * @param RangeSet|null $rangeSet Range header on which the transmission will be based
118
     * @param OutputWriter|null $outputWriter Output writer via which resource will be sent
119
     */
120
    public function sendResource(RangeSet $rangeSet = null, OutputWriter $outputWriter = null)
121
    {
122
        $outputWriter = $outputWriter ?? new DefaultOutputWriter();
123
        $headers = $this->generateDefaultHeaders();
124
125
        if ($rangeSet === null) {
126
            $this->sendCompleteResource($outputWriter, $headers);
127
        } else {
128
            $this->sendResourceRanges($outputWriter, $headers, $rangeSet);
129
        }
130
    }
131
}
132