From 9cc753850c667dd4320d032bbd5cf4f86329760d Mon Sep 17 00:00:00 2001 From: Duccio Gasparri Date: Tue, 29 May 2012 19:11:01 +0200 Subject: [PATCH] \Http\Response\Stream honoring the Content-Length header --- src/Response/Stream.php | 155 ++++++++++++++++++++++++++++++---------- 1 file changed, 119 insertions(+), 36 deletions(-) diff --git a/src/Response/Stream.php b/src/Response/Stream.php index f6d5e461eb..08dc4c64ae 100755 --- a/src/Response/Stream.php +++ b/src/Response/Stream.php @@ -21,7 +21,9 @@ namespace Zend\Http\Response; -use Zend\Http\Response; +use Zend\Http\Response, + Zend\Http\Exception; + /** * Zend_Http_Response represents an HTTP 1.0 / 1.1 response message. It @@ -35,6 +37,21 @@ */ class Stream extends Response { + + /** + * The Content-Length value, if set + * + * @var int + */ + protected $contentLength = null; + + /** + * The portion of the body that has alredy been streamed + * + * @var int + */ + protected $contentStreamed = 0; + /** * Response as stream * @@ -56,7 +73,7 @@ class Stream extends Response * * @var boolean */ - protected $_cleanup; + protected $cleanup; /** * Get the response as stream @@ -87,7 +104,7 @@ public function setStream($stream) */ public function getCleanup() { - return $this->_cleanup; + return $this->cleanup; } /** @@ -97,7 +114,7 @@ public function getCleanup() */ public function setCleanup($cleanup = true) { - $this->_cleanup = $cleanup; + $this->cleanup = $cleanup; } /** @@ -122,24 +139,79 @@ public function setStreamName($streamName) return $this; } - /** - * Create a new Zend\Http\Response\Stream object from a string + + /** + * Create a new Zend\Http\Response\Stream object from a stream * - * @param string $responseString - * @param resource $stream - * @return Stream - */ - public static function fromStream($responseString, $stream) - { + * @param string $responseString + * @param resource $stream + * @return Stream + */ + public static function fromStream($responseString, $stream) + { + + if (!is_resource($stream)) { + throw new Exception\InvalidArgumentException('A valid stream is required'); + } - $response = static::fromString($responseString); - if (is_resource($stream)) { - $response->setStream($stream); - } + $headerComplete = false; + $headersString = ''; + + $responseArray = explode("\n",$responseString); - return $response; + while (count($responseArray)) { + $nextLine = array_shift($responseArray); + $headersString .= $nextLine."\n"; + $nextLineTrimmed = trim($nextLine); + if ($nextLineTrimmed == "") { + $headerComplete = true; + break; + } + + } + + if (!$headerComplete) { + while (false !== ($nextLine = fgets($stream))) { + + $headersString .= trim($nextLine)."\r\n"; + if ($nextLine == "\r\n" || $nextLine == "\n") { + $headerComplete = true; + break; + } + } + } + + if (!$headerComplete) { + throw new Exception\OutOfRangeException('End of header not found'); + } + + $response = static::fromString($headersString); + + if (is_resource($stream)) { + $response->setStream($stream); + } + + if (count($responseArray)) { + $response->content = implode("\n", $responseArray); + } + + $headers = $response->headers(); + foreach($headers as $header) { + if ($header instanceof \Zend\Http\Header\ContentLength) { + $response->contentLength = (int) $header->getFieldValue(); + if (strlen($response->content) > $response->contentLength) { + throw new Exception\OutOfRangeException( + sprintf('Too much content was extracted from the stream (%d instead of %d bytes)', + strlen($this->content), $this->contentLength)); + } + break; + } + } + + return $response; } - + + /** * Get the response body as string * @@ -176,23 +248,34 @@ public function getRawBody() return $this->content; } - /** - * Read stream content and return it as string - * - * Function reads the remainder of the body from the stream and closes the stream. - * - * @return string - */ - protected function readStream() - { - if (!is_resource($this->stream)) { - return ''; - } - - $this->content .= stream_get_contents($this->stream); - $this->stream = null; //Could be listened by others - } - + + /** + * Read stream content and return it as string + * + * Function reads the remainder of the body from the stream and closes the stream. + * + * @return string + */ + protected function readStream() + { + if (!is_null($this->contentLength)) { + $bytes = $this->contentLength - $this->contentStreamed; + } else { + $bytes = -1; //Read the whole buffer + } + + if (!is_resource($this->stream) || $bytes == 0) { + return ''; + } + + $this->content .= stream_get_contents($this->stream, $bytes); + $this->contentStreamed += strlen($this->content); + + if ($this->contentLength == $this->contentStreamed) { + $this->stream = null; + } + } + /** * Destructor */ @@ -201,7 +284,7 @@ public function __destruct() if (is_resource($this->stream)) { $this->stream = null; //Could be listened by others } - if ($this->_cleanup) { + if ($this->cleanup) { @unlink($this->stream_name); } }