Merge branch 'requestLimit' of github.com:SoftCreatR/WCF into SoftCreatR-requestLimit
authorTim Düsterhus <duesterhus@woltlab.com>
Thu, 20 Mar 2014 22:23:39 +0000 (23:23 +0100)
committerTim Düsterhus <duesterhus@woltlab.com>
Thu, 20 Mar 2014 22:23:39 +0000 (23:23 +0100)
Conflicts:
wcfsetup/install/files/lib/util/HTTPRequest.class.php

1  2 
wcfsetup/install/files/lib/util/HTTPRequest.class.php

index ef028482b4a9391ed6467240460d16b1130dd86f,f638c14b7f2037430365bb80f189b428a7908fab..fc3b61f8d4de1a89d859545428b065d441ae6000
@@@ -127,10 -121,14 +127,14 @@@ final class HTTPRequest 
                $this->setOptions($options);
                
                // set default headers
 -              $this->addHeader('User-Agent', "HTTP.PHP (HTTPRequest.class.php; WoltLab Community Framework/".WCF_VERSION."; ".WCF::getLanguage()->languageCode.")");
 -              $this->addHeader('Accept', '*/*');
 -              $this->addHeader('Accept-Language', WCF::getLanguage()->getFixedLanguageCode());
 +              $this->addHeader('user-agent', "HTTP.PHP (HTTPRequest.class.php; WoltLab Community Framework/".WCF_VERSION."; ".WCF::getLanguage()->languageCode.")");
 +              $this->addHeader('accept', '*/*');
 +              $this->addHeader('accept-language', WCF::getLanguage()->getFixedLanguageCode());
                
 -                      $this->addHeader('Range', 'bytes=0-'.$this->options['maxLength']);
+               if (isset($this->options['maxLength'])) {
++                      $this->addHeader('Range', 'bytes=0-'.($this->options['maxLength'] - 1));
+               }
+               
                if ($this->options['method'] !== 'GET') {
                        if (empty($this->files)) {
                                if (is_array($postParameters)) {
                $inHeader = true;
                $this->replyHeaders = array();
                $this->replyBody = '';
 +              $chunkLength = 0;
++              $bodyLength = 0;
                
                // read http response.
                while (!$remoteFile->eof()) {
 -                      $line = $remoteFile->gets();
 +                      if ($chunkLength) {
++                              if (isset($this->options['maxLength'])) $chunkLength = min($chunkLength, $this->options['maxLength'] - $bodyLength);
 +                              $line = $remoteFile->read($chunkLength);
 +                      }
 +                      else {
 +                              $line = $remoteFile->gets();
 +                      }
 +                      
                        if ($inHeader) {
                                if (rtrim($line) === '') {
                                        $inHeader = false;
                                $this->replyHeaders[] = $line;
                        }
                        else {
 -                              $this->replyBody .= $line;
 -                              $bodyLength = strlen($line);
 +                              $chunkedTransferRegex = new Regex('(^|,)[ \t]*chunked[ \t]*$', Regex::CASE_INSENSITIVE);
 +                              if (isset($this->replyHeaders['transfer-encoding']) && $chunkedTransferRegex->match(end($this->replyHeaders['transfer-encoding']))) {
 +                                      // remove chunked from transfer-encoding
 +                                      $this->replyHeaders['transfer-encoding'] = array_filter(array_map(function ($element) use ($chunkedTransferRegex) {
 +                                              return $chunkedTransferRegex->replace($element, '');
 +                                      }, $this->replyHeaders['transfer-encoding']), 'trim');
 +                                      if (empty($this->replyHeaders['transfer-encoding'])) unset($this->replyHeaders['transfer-encoding']);
 +                                      
 +                                      // last chunk finished
 +                                      if ($chunkLength === 0) {
 +                                              // read hex data and trash chunk-extension
 +                                              list($hex) = explode(';', $line, 2);
 +                                              $chunkLength = hexdec($hex);
 +                                              
 +                                              // $chunkLength === 0 -> no more data
 +                                              if ($chunkLength === 0) {
 +                                                      // clear remaining response
 +                                                      while (!$remoteFile->gets());
 +                                                      
 +                                                      // break out of main reading loop
 +                                                      break;
 +                                              }
 +                                      }
 +                                      else {
 +                                              $this->replyBody .= $line;
 +                                              $chunkLength -= strlen($line);
++                                              $bodyLength = +strlen($line);
 +                                              $remoteFile->read(2); // CRLF
 +                                      }
 +                              }
 +                              else {
 +                                      $this->replyBody .= $line;
++                                      $bodyLength = +strlen($line);
++                              }
+                               
+                               if (isset($this->options['maxLength']) && $bodyLength >= $this->options['maxLength']) {
+                                       break;
                                }
                        }
                }
                
++              if (isset($this->options['maxLength'])) $this->replyBody = substr($this->replyBody, 0, $this->options['maxLength']);
++              
 +              $remoteFile->close();
 +              
                $this->parseReply();
        }
        
                
                // get status code
                $statusLine = reset($this->replyHeaders);
 -              $regex = new Regex('^HTTP/1.[01] (\d{3})');
 -              if (!$regex->match($statusLine)) throw new SystemException("Unexpected status '".$statusLine."'");
 +              $regex = new Regex('^HTTP/1.\d+ +(\d{3})');
 +              if (!$regex->match($statusLine[0])) throw new SystemException("Unexpected status '".$statusLine."'");
                $matches = $regex->getMatches();
                $this->statusCode = $matches[1];
 -              
 -              // validate length
 -              if (isset($this->replyHeaders['Content-Length']) && !isset($this->options['maxLength'])) {
 -                      if (strlen($this->replyBody) != $this->replyHeaders['Content-Length']) {
 +      }
 +      
 +      /**
 +       * Parses the reply.
 +       */
 +      private function parseReply() {
 +              // 4.4 Messages MUST NOT include both a Content-Length header field and a
 +              // non-identity transfer-coding. If the message does include a non-
 +              // identity transfer-coding, the Content-Length MUST be ignored.
-               if (isset($this->replyHeaders['content-length']) && (!isset($this->replyHeaders['transfer-encoding']) || strtolower(end($this->replyHeaders['transfer-encoding'])) !== 'identity')) {
++              if (isset($this->replyHeaders['content-length']) && (!isset($this->replyHeaders['transfer-encoding']) || strtolower(end($this->replyHeaders['transfer-encoding'])) !== 'identity') && !isset($this->options['maxLength'])) {
 +                      if (strlen($this->replyBody) != $this->replyHeaders['content-length']) {
                                throw new SystemException('Body length does not match length given in header');
                        }
                }
                                return;
                        break;
                        
 -                      case '200':
 -                      case '204':
 -                              // we are fine
 -                      break;
 -                      
+                       case '206':
+                               // check, if partial content was expected
 -                              if (!isset($this->headers['Range'])) {
++                              if (!isset($this->headers['range'])) {
+                                       throw new HTTPServerErrorException("Received unexpected status code '206' from server");
+                               }
 -                              else if (!isset($this->replyHeaders['Content-Range'])) {
++                              else if (!isset($this->replyHeaders['content-range'])) {
+                                       throw new HTTPServerErrorException("Content-Range is missing in reply header");
+                               }
+                       break;
+                       
                        case '401':
                        case '403':
                                throw new HTTPUnauthorizedException("Received status code '".$this->statusCode."' from server");