From: Tim Düsterhus Date: Thu, 20 Mar 2014 22:23:39 +0000 (+0100) Subject: Merge branch 'requestLimit' of github.com:SoftCreatR/WCF into SoftCreatR-requestLimit X-Git-Tag: 2.1.0_Alpha_1~995 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=5262964b320ac82e48627b37c42bb4e91a665d08;p=GitHub%2FWoltLab%2FWCF.git Merge branch 'requestLimit' of github.com:SoftCreatR/WCF into SoftCreatR-requestLimit Conflicts: wcfsetup/install/files/lib/util/HTTPRequest.class.php --- 5262964b320ac82e48627b37c42bb4e91a665d08 diff --cc wcfsetup/install/files/lib/util/HTTPRequest.class.php index ef028482b4,f638c14b7f..fc3b61f8d4 --- a/wcfsetup/install/files/lib/util/HTTPRequest.class.php +++ b/wcfsetup/install/files/lib/util/HTTPRequest.class.php @@@ -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()); + if (isset($this->options['maxLength'])) { - $this->addHeader('Range', 'bytes=0-'.$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)) { @@@ -244,17 -241,11 +248,19 @@@ $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; @@@ -265,43 -254,15 +271,51 @@@ $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(); } @@@ -338,21 -284,14 +352,21 @@@ // 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'); } } @@@ -407,6 -347,21 +421,16 @@@ 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");