Completed
Push — master ( ecdfec...e41ddb )
by Ankit
02:20
created

Server::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace TusPhp\Tus;
4
5
use TusPhp\File;
6
use TusPhp\Request;
7
use TusPhp\Response;
8
use TusPhp\Cache\Cacheable;
9
use TusPhp\Exception\FileException;
10
use TusPhp\Exception\ConnectionException;
11
use Illuminate\Http\Response as HttpResponse;
12
use Symfony\Component\HttpFoundation\BinaryFileResponse;
13
14
class Server extends AbstractTus
15
{
16
    /** @const Tus Creation Extension */
17
    const TUS_EXTENSION_CREATION = 'creation';
18
19
    /** @const Tus Termination Extension */
20
    const TUS_EXTENSION_TERMINATION = 'termination';
21
22
    /** @var Request */
23
    protected $request;
24
25
    /** @var Response */
26
    protected $response;
27
28
    /** @var string */
29
    protected $uploadDir;
30
31
    /**
32
     * TusServer constructor.
33
     *
34
     * @param Cacheable|string $cacheAdapter
35
     */
36 3
    public function __construct($cacheAdapter = 'file')
37
    {
38 3
        $this->request   = new Request();
39 3
        $this->response  = new Response();
40 3
        $this->uploadDir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'uploads';
41
42 3
        $this->setCache($cacheAdapter);
43 3
    }
44
45
    /**
46
     * Set upload dir.
47
     *
48
     * @param string $path
49
     *
50
     * @return void
51
     */
52 1
    public function setUploadDir(string $path)
53
    {
54 1
        $this->uploadDir = $path;
55 1
    }
56
57
    /**
58
     * Get upload dir.
59
     *
60
     * @return string
61
     */
62 1
    public function getUploadDir() : string
63
    {
64 1
        return $this->uploadDir;
65
    }
66
67
    /**
68
     * Get request.
69
     *
70
     * @return Request
71
     */
72 1
    public function getRequest() : Request
73
    {
74 1
        return $this->request;
75
    }
76
77
    /**
78
     * Get request.
79
     *
80
     * @return Response
81
     */
82 1
    public function getResponse() : Response
83
    {
84 1
        return $this->response;
85
    }
86
87
    /**
88
     * Handle all HTTP request.
89
     *
90
     * @return null|HttpResponse
91
     */
92 2
    public function serve()
93
    {
94 2
        $method = $this->getRequest()->method();
95
96 2
        if (! in_array($method, $this->request->allowedHttpVerbs())) {
97 1
            return $this->response->send(null, HttpResponse::HTTP_METHOD_NOT_ALLOWED);
98
        }
99
100 1
        $method = 'handle' . ucfirst(strtolower($method));
101
102 1
        $this->{$method}();
103
104 1
        $this->exit();
105 1
    }
106
107
    /**
108
     * Exit from current php process.
109
     *
110
     * @codeCoverageIgnore
111
     */
112
    protected function exit()
113
    {
114
        exit(0);
115
    }
116
117
    /**
118
     * Handle OPTIONS request.
119
     *
120
     * @return HttpResponse
121
     */
122 1
    protected function handleOptions() : HttpResponse
123
    {
124 1
        return $this->response->send(
125 1
            null,
126 1
            HttpResponse::HTTP_OK,
127
            [
128 1
                'Allow' => $this->request->allowedHttpVerbs(),
129 1
                'Tus-Version' => self::TUS_PROTOCOL_VERSION,
130 1
                'Tus-Extension' => self::TUS_EXTENSION_CREATION . ',' . self::TUS_EXTENSION_TERMINATION,
131
            ]
132
        );
133
    }
134
135
    /**
136
     * Handle HEAD request.
137
     *
138
     * @return HttpResponse
139
     */
140 3
    protected function handleHead() : HttpResponse
141
    {
142 3
        $checksum = $this->request->checksum();
143
144 3
        if (! $this->cache->get($checksum)) {
145 1
            return $this->response->send(null, HttpResponse::HTTP_NOT_FOUND);
146
        }
147
148 2
        $offset = $this->cache->get($checksum)['offset'] ?? false;
149
150 2
        if (false === $offset) {
151 1
            return $this->response->send(null, HttpResponse::HTTP_GONE);
152
        }
153
154 1
        return $this->response->send(null, HttpResponse::HTTP_OK, [
155 1
            'Upload-Offset' => (int) $offset,
156 1
            'Cache-Control' => 'no-store',
157 1
            'Tus-Resumable' => self::TUS_PROTOCOL_VERSION,
158
        ]);
159
    }
160
161
    /**
162
     * Handle POST request.
163
     *
164
     * @return HttpResponse
165
     */
166 2
    protected function handlePost() : HttpResponse
167
    {
168 2
        $fileName = $this->getRequest()->extractFileName();
169
170 2
        if (empty($fileName)) {
171 1
            return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
172
        }
173
174 1
        $checksum = $this->getRequest()->header('Checksum');
175 1
        $location = $this->getRequest()->url() . "/" . basename($this->uploadDir) . "/" . $fileName;
176
177 1
        $file = $this->buildFile([
178 1
            'name' => $fileName,
179 1
            'offset' => 0,
180 1
            'size' => $this->getRequest()->header('Upload-Length'),
181 1
            'file_path' => $this->uploadDir . DIRECTORY_SEPARATOR . $fileName,
182 1
            'location' => $location,
183 1
        ])->setChecksum($checksum);
184
185 1
        $this->cache->set($checksum, $file->details());
186
187 1
        return $this->response->send(
188 1
            ['data' => ['checksum' => $checksum]],
189 1
            HttpResponse::HTTP_CREATED,
190
            [
191 1
                'Location' => $location,
192 1
                'Upload-Expires' => $this->cache->get($checksum)['expires_at'],
193 1
                'Tus-Resumable' => self::TUS_PROTOCOL_VERSION,
194
            ]
195
        );
196
    }
197
198
    /**
199
     * Handle PATCH request.
200
     *
201
     * @return HttpResponse
202
     */
203 4
    protected function handlePatch() : HttpResponse
204
    {
205 4
        $checksum = $this->request->checksum();
206
207 4
        if (! $this->cache->get($checksum)) {
208 1
            return $this->response->send(null, HttpResponse::HTTP_GONE);
209
        }
210
211 3
        $meta = $this->cache->get($checksum);
212 3
        $file = $this->buildFile($meta);
213
214
        try {
215 3
            $offset = $file->setChecksum($checksum)->upload($file->getFileSize());
216 2
        } catch (FileException $e) {
217 1
            return $this->response->send(null, HttpResponse::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE);
218 1
        } catch (ConnectionException $e) {
219 1
            return $this->response->send(null, HttpResponse::HTTP_CONTINUE);
220
        }
221
222 1
        return $this->response->send(null, HttpResponse::HTTP_NO_CONTENT, [
223 1
            'Upload-Expires' => $this->cache->get($checksum)['expires_at'],
224 1
            'Upload-Offset' => $offset,
225 1
            'Tus-Resumable' => self::TUS_PROTOCOL_VERSION,
226
        ]);
227
    }
228
229
    /**
230
     * Handle GET request.
231
     *
232
     * @return BinaryFileResponse|HttpResponse
233
     */
234 4
    protected function handleGet()
235
    {
236 4
        $checksum = $this->request->checksum();
237
238 4
        if (! $checksum) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $checksum of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
239 1
            return $this->response->send('400 bad request.', HttpResponse::HTTP_BAD_REQUEST);
240
        }
241
242 3
        $fileMeta = $this->cache->get($checksum);
243
244 3
        if (! $fileMeta) {
245 1
            return $this->response->send('404 upload not found.', HttpResponse::HTTP_NOT_FOUND);
246
        }
247
248 2
        $resource = $fileMeta['file_path'] ?? null;
249 2
        $fileName = $fileMeta['name'] ?? null;
250
251 2
        if (! $resource || ! file_exists($resource)) {
252 1
            return $this->response->send('404 upload not found.', HttpResponse::HTTP_NOT_FOUND);
253
        }
254
255 1
        return $this->response->download($resource, $fileName);
256
    }
257
258
    /**
259
     * Handle DELETE request.
260
     *
261
     * @return HttpResponse
262
     */
263 3
    protected function handleDelete() : HttpResponse
264
    {
265 3
        $checksum = $this->request->checksum();
266 3
        $fileMeta = $this->cache->get($checksum);
267 3
        $resource = $fileMeta['file_path'] ?? null;
268
269 3
        if (! $resource) {
270 1
            return $this->response->send(null, HttpResponse::HTTP_NOT_FOUND);
271
        }
272
273 2
        $isDeleted = $this->cache->delete($checksum);
274
275 2
        if (! $isDeleted || ! file_exists($resource)) {
276 1
            return $this->response->send(null, HttpResponse::HTTP_GONE);
277
        }
278
279 1
        return $this->response->send(null, HttpResponse::HTTP_NO_CONTENT, [
280 1
            'Tus-Resumable' => self::TUS_PROTOCOL_VERSION,
281 1
            'Tus-Extension' => self::TUS_EXTENSION_TERMINATION,
282
        ]);
283
    }
284
285
    /**
286
     * Build file object.
287
     *
288
     * @param array $meta
289
     *
290
     * @return File
291
     */
292 1
    protected function buildFile(array $meta) : File
293
    {
294 1
        return (new File($meta['name'], $this->cache))
295 1
            ->setMeta($meta['offset'], $meta['size'], $meta['file_path'], $meta['location']);
296
    }
297
298
    /**
299
     * No other methods are allowed.
300
     *
301
     * @param string $method
302
     * @param array  $params
303
     *
304
     * @return HttpResponse|BinaryFileResponse
305
     */
306 1
    public function __call(string $method, array $params)
307
    {
308 1
        return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
309
    }
310
}
311