Skip to content

Commit aacebf2

Browse files
committed
bug #38516 [HttpFoundation] Fix Range Requests (BattleRattle)
This PR was merged into the 3.4 branch. Discussion ---------- [HttpFoundation] Fix Range Requests | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | #38295 | License | MIT | Doc PR | This PR fixes some deviations from [RFC 7233](https://tools.ietf.org/html/rfc7233) for handling range requests, mentioned in #38295. - overlapping ranges are now satisfiable (e.g. when requested range end is larger than the file size) - range units other than `bytes` will get ignored - range requests for methods other than `GET` will be ignored I did not manage yet to implement the support for multiple ranges, but also don't know, if that's needed here. Commits ------- 681804ba1a [HttpFoundation] Fix Range Requests
2 parents d18c26c + f445ee1 commit aacebf2

File tree

2 files changed

+47
-21
lines changed

2 files changed

+47
-21
lines changed

BinaryFileResponse.php

+23-20
Original file line numberDiff line numberDiff line change
@@ -239,33 +239,36 @@ public function prepare(Request $request)
239239
$this->headers->set($type, $path);
240240
$this->maxlen = 0;
241241
}
242-
} elseif ($request->headers->has('Range')) {
242+
} elseif ($request->headers->has('Range') && $request->isMethod('GET')) {
243243
// Process the range headers.
244244
if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
245245
$range = $request->headers->get('Range');
246246

247-
list($start, $end) = explode('-', substr($range, 6), 2) + [0];
247+
if (0 === strpos($range, 'bytes=')) {
248+
list($start, $end) = explode('-', substr($range, 6), 2) + [0];
248249

249-
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
250+
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
250251

251-
if ('' === $start) {
252-
$start = $fileSize - $end;
253-
$end = $fileSize - 1;
254-
} else {
255-
$start = (int) $start;
256-
}
252+
if ('' === $start) {
253+
$start = $fileSize - $end;
254+
$end = $fileSize - 1;
255+
} else {
256+
$start = (int) $start;
257+
}
257258

258-
if ($start <= $end) {
259-
if ($start < 0 || $end > $fileSize - 1) {
260-
$this->setStatusCode(416);
261-
$this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
262-
} elseif (0 !== $start || $end !== $fileSize - 1) {
263-
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
264-
$this->offset = $start;
265-
266-
$this->setStatusCode(206);
267-
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
268-
$this->headers->set('Content-Length', $end - $start + 1);
259+
if ($start <= $end) {
260+
$end = min($end, $fileSize - 1);
261+
if ($start < 0 || $start > $end) {
262+
$this->setStatusCode(416);
263+
$this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
264+
} elseif ($end - $start < $fileSize - 1) {
265+
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
266+
$this->offset = $start;
267+
268+
$this->setStatusCode(206);
269+
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
270+
$this->headers->set('Content-Length', $end - $start + 1);
271+
}
269272
}
270273
}
271274
}

Tests/BinaryFileResponseTest.php

+24-1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public function provideRanges()
149149
['bytes=30-', 30, 5, 'bytes 30-34/35'],
150150
['bytes=30-30', 30, 1, 'bytes 30-30/35'],
151151
['bytes=30-34', 30, 5, 'bytes 30-34/35'],
152+
['bytes=30-40', 30, 5, 'bytes 30-34/35'],
152153
];
153154
}
154155

@@ -203,9 +204,31 @@ public function provideFullFileRanges()
203204
// Syntactical invalid range-request should also return the full resource
204205
['bytes=20-10'],
205206
['bytes=50-40'],
207+
// range units other than bytes must be ignored
208+
['unknown=10-20'],
206209
];
207210
}
208211

212+
public function testRangeOnPostMethod()
213+
{
214+
$request = Request::create('/', 'POST');
215+
$request->headers->set('Range', 'bytes=10-20');
216+
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
217+
218+
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
219+
$data = fread($file, 35);
220+
fclose($file);
221+
222+
$this->expectOutputString($data);
223+
$response = clone $response;
224+
$response->prepare($request);
225+
$response->sendContent();
226+
227+
$this->assertSame(200, $response->getStatusCode());
228+
$this->assertSame('35', $response->headers->get('Content-Length'));
229+
$this->assertNull($response->headers->get('Content-Range'));
230+
}
231+
209232
public function testUnpreparedResponseSendsFullFile()
210233
{
211234
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200);
@@ -242,7 +265,7 @@ public function provideInvalidRanges()
242265
{
243266
return [
244267
['bytes=-40'],
245-
['bytes=30-40'],
268+
['bytes=40-50'],
246269
];
247270
}
248271

0 commit comments

Comments
 (0)