Fix `Range` handling in \wcf\util\FileReader
authorTim Düsterhus <duesterhus@woltlab.com>
Sun, 23 Apr 2017 14:20:24 +0000 (16:20 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Sun, 23 Apr 2017 14:26:44 +0000 (16:26 +0200)
- Disallow invalid Range: bytes=100 without a trailing hyphen.
- Disallow invalid Range: bytes=2-1 with start > end.
- Support valid    Range: bytes=0-0 with end = 0.
- Support maximum offset > filesize as per RFC 7233:
   A client can limit the number of bytes requested without knowing the
   size of the selected representation.  If the last-byte-pos value is
   absent, or if the value is greater than or equal to the current
   length of the representation data, the byte range is interpreted as
   the remainder of the representation (i.e., the server replaces the
   value of last-byte-pos with a value that is one less than the current
   length of the selected representation).

wcfsetup/install/files/lib/util/FileReader.class.php

index 14625bd9ae62b2407e0381a89752205af6972e08..a339f4f7217c5d8adf9d81a3ca7a4283ffa67fac 100644 (file)
@@ -105,20 +105,25 @@ class FileReader {
                $this->endByte = $this->options['filesize'] - 1;
                if ($this->options['enableRangeSupport']) {
                        if (!empty($_SERVER['HTTP_RANGE'])) {
-                               $regex = new Regex('^bytes=(-?\d+)(?:-(\d+))?$');
+                               $regex = new Regex('^bytes=(?:(\d+)-(\d+)?|-(\d+))$');
                                if ($regex->match($_SERVER['HTTP_RANGE'])) {
                                        $matches = $regex->getMatches();
-                                       $first = intval($matches[1]);
-                                       $last = (isset($matches[2]) ? intval($matches[2]) : 0);
+                                       $start = (isset($matches[1]) && $matches[1] !== '' ? intval($matches[1]) : null);
+                                       $end = (isset($matches[2]) && $matches[2] !== '' ? intval($matches[2]) : null);
+                                       $last = (isset($matches[3]) && $matches[3] !== '' ? intval($matches[3]) : null);
                                        
-                                       if ($first < 0) {
-                                               // negative value; subtract from filesize
-                                               $this->startByte = $this->options['filesize'] + $first;
+                                       if ($start !== null) {
+                                               $this->startByte = $start;
                                        }
-                                       else {
-                                               $this->startByte = $first;
-                                               if ($last > 0) {
-                                                       $this->endByte = $last;
+                                       if ($end !== null) {
+                                               if ($end <= ($this->options['filesize'] - 1)) {
+                                                       $this->endByte = $end;
+                                               }
+                                       }
+                                       if ($start === null && $end === null && $last !== null) {
+                                               if ($last <= $this->options['filesize']) {
+                                                       // negative value; subtract from filesize
+                                                       $this->startByte = $this->options['filesize'] - $last;
                                                }
                                        }
                                }
@@ -130,7 +135,7 @@ class FileReader {
         * Handles the given header items.
         */
        protected function handleHeaders() {
-               if ($this->startByte < 0 || $this->startByte >= $this->options['filesize'] || $this->endByte >= $this->options['filesize']) {
+               if ($this->startByte < 0 || $this->startByte >= $this->options['filesize'] || $this->endByte < $this->startByte) {
                        // invalid range given
                        $this->addHeader('', 'HTTP/1.1 416 Requested Range Not Satisfiable');
                        $this->addHeader('Accept-Ranges', 'bytes');