/** * Read a part of response body encoded with chunked Transfer-Encoding * * @access private * @return string */ function _readChunked() { // at start of the next chunk? if (0 == $this->_chunkLength) { $line = $this->_sock->readLine(); if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) { $this->_chunkLength = hexdec($matches[1]); // Chunk with zero length indicates the end if (0 == $this->_chunkLength) { $this->_sock->readAll(); // make this an eof() return ''; } } elseif ($this->_sock->eof()) { return ''; } } $data = $this->_sock->read($this->_chunkLength); $this->_chunkLength -= strlen($data); if (0 == $this->_chunkLength) { $this->_sock->readLine(); // Trailing CRLF } return $data; }
/** * Processes a HTTP response * * This extracts response code, headers, cookies and decodes body if it * was encoded in some way * * @access public * @param bool Whether to store response body in object property, set * this to false if downloading a LARGE file and using a Listener. * This is assumed to be true if body is gzip-encoded. * @throws PEAR_Error * @return mixed true on success, PEAR_Error in case of malformed response */ function process($saveBody = true) { do { $line = $this->_sock->readLine(); if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) { return PEAR::raiseError('Malformed response.'); } else { $this->_protocol = 'HTTP/' . $http_version; $this->_code = intval($returncode); } while ('' !== ($header = $this->_sock->readLine())) { $this->_processHeader($header); } } while (100 == $this->_code); $this->_notify('gotHeaders', $this->_headers); // If response body is present, read it and decode $chunked = isset($this->_headers['transfer-encoding']) && 'chunked' == $this->_headers['transfer-encoding']; $gzipped = isset($this->_headers['content-encoding']) && 'gzip' == $this->_headers['content-encoding']; $hasBody = false; if (!isset($this->_headers['content-length']) || 0 != $this->_headers['content-length']) { while (!$this->_sock->eof()) { if ($chunked) { $data = $this->_readChunked(); } else { $data = $this->_sock->read(4096); } if ('' == $data) { break; } else { $hasBody = true; if ($saveBody || $gzipped) { $this->_body .= $data; } $this->_notify($gzipped ? 'gzTick' : 'tick', $data); } } } if ($hasBody) { // Uncompress the body if needed if ($gzipped) { $this->_body = gzinflate(substr($this->_body, 10)); $this->_notify('gotBody', $this->_body); } else { $this->_notify('gotBody'); } } return true; }
/** * Processes a HTTP response * * This extracts response code, headers, cookies and decodes body if it * was encoded in some way * * @access public * @param bool Whether to store response body in object property, set * this to false if downloading a LARGE file and using a Listener. * This is assumed to be true if body is gzip-encoded. * @param bool Whether the response can actually have a message-body. * Will be set to false for HEAD requests. * @throws PEAR_Error * @return mixed true on success, PEAR_Error in case of malformed response */ function process($saveBody = true, $canHaveBody = true) { do { $line = $this->_sock->readLine(); if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) { return PEAR::raiseError('Malformed response.'); } else { $this->_protocol = 'HTTP/' . $http_version; $this->_code = intval($returncode); } while ('' !== ($header = $this->_sock->readLine())) { $this->_processHeader($header); } } while (100 == $this->_code); $this->_notify('gotHeaders', $this->_headers); // RFC 2616, section 4.4: // 1. Any response message which "MUST NOT" include a message-body ... // is always terminated by the first empty line after the header fields // 3. ... If a message is received with both a // Transfer-Encoding header field and a Content-Length header field, // the latter MUST be ignored. $canHaveBody = $canHaveBody && $this->_code >= 200 && $this->_code != 204 && $this->_code != 304; // If response body is present, read it and decode $chunked = isset($this->_headers['transfer-encoding']) && 'chunked' == $this->_headers['transfer-encoding']; $gzipped = isset($this->_headers['content-encoding']) && 'gzip' == $this->_headers['content-encoding']; $hasBody = false; if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) || 0 != $this->_headers['content-length'])) { if ($chunked || !isset($this->_headers['content-length'])) { $this->_toRead = null; } else { $this->_toRead = $this->_headers['content-length']; } while (!$this->_sock->eof() && (is_null($this->_toRead) || 0 < $this->_toRead)) { if ($chunked) { $data = $this->_readChunked(); } elseif (is_null($this->_toRead)) { $data = $this->_sock->read(4096); } else { $data = $this->_sock->read(min(4096, $this->_toRead)); $this->_toRead -= strlen($data); } if ('' == $data) { break; } else { $hasBody = true; if ($saveBody || $gzipped) { $this->_body .= $data; } $this->_notify($gzipped ? 'gzTick' : 'tick', $data); } } } if ($hasBody) { // Uncompress the body if needed if ($gzipped) { $body = $this->_decodeGzip($this->_body); if (PEAR::isError($body)) { return $body; } $this->_body = $body; $this->_notify('gotBody', $this->_body); } else { $this->_notify('gotBody'); } } return true; }