PageRenderTime 39ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/Zend/Http/Response.php

https://bitbucket.org/sunil_nextbits/magento2
PHP | 667 lines | 360 code | 70 blank | 237 comment | 46 complexity | 0539f26a79ab42d05abb668c434bd413 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Http
  17. * @subpackage Response
  18. * @version $Id: Response.php 22810 2010-08-08 10:29:09Z shahar $
  19. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  20. * @license http://framework.zend.com/license/new-bsd New BSD License
  21. */
  22. /**
  23. * Zend_Http_Response represents an HTTP 1.0 / 1.1 response message. It
  24. * includes easy access to all the response's different elemts, as well as some
  25. * convenience methods for parsing and validating HTTP responses.
  26. *
  27. * @package Zend_Http
  28. * @subpackage Response
  29. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  30. * @license http://framework.zend.com/license/new-bsd New BSD License
  31. */
  32. class Zend_Http_Response
  33. {
  34. /**
  35. * List of all known HTTP response codes - used by responseCodeAsText() to
  36. * translate numeric codes to messages.
  37. *
  38. * @var array
  39. */
  40. protected static $messages = array(
  41. // Informational 1xx
  42. 100 => 'Continue',
  43. 101 => 'Switching Protocols',
  44. // Success 2xx
  45. 200 => 'OK',
  46. 201 => 'Created',
  47. 202 => 'Accepted',
  48. 203 => 'Non-Authoritative Information',
  49. 204 => 'No Content',
  50. 205 => 'Reset Content',
  51. 206 => 'Partial Content',
  52. // Redirection 3xx
  53. 300 => 'Multiple Choices',
  54. 301 => 'Moved Permanently',
  55. 302 => 'Found', // 1.1
  56. 303 => 'See Other',
  57. 304 => 'Not Modified',
  58. 305 => 'Use Proxy',
  59. // 306 is deprecated but reserved
  60. 307 => 'Temporary Redirect',
  61. // Client Error 4xx
  62. 400 => 'Bad Request',
  63. 401 => 'Unauthorized',
  64. 402 => 'Payment Required',
  65. 403 => 'Forbidden',
  66. 404 => 'Not Found',
  67. 405 => 'Method Not Allowed',
  68. 406 => 'Not Acceptable',
  69. 407 => 'Proxy Authentication Required',
  70. 408 => 'Request Timeout',
  71. 409 => 'Conflict',
  72. 410 => 'Gone',
  73. 411 => 'Length Required',
  74. 412 => 'Precondition Failed',
  75. 413 => 'Request Entity Too Large',
  76. 414 => 'Request-URI Too Long',
  77. 415 => 'Unsupported Media Type',
  78. 416 => 'Requested Range Not Satisfiable',
  79. 417 => 'Expectation Failed',
  80. // Server Error 5xx
  81. 500 => 'Internal Server Error',
  82. 501 => 'Not Implemented',
  83. 502 => 'Bad Gateway',
  84. 503 => 'Service Unavailable',
  85. 504 => 'Gateway Timeout',
  86. 505 => 'HTTP Version Not Supported',
  87. 509 => 'Bandwidth Limit Exceeded'
  88. );
  89. /**
  90. * The HTTP version (1.0, 1.1)
  91. *
  92. * @var string
  93. */
  94. protected $version;
  95. /**
  96. * The HTTP response code
  97. *
  98. * @var int
  99. */
  100. protected $code;
  101. /**
  102. * The HTTP response code as string
  103. * (e.g. 'Not Found' for 404 or 'Internal Server Error' for 500)
  104. *
  105. * @var string
  106. */
  107. protected $message;
  108. /**
  109. * The HTTP response headers array
  110. *
  111. * @var array
  112. */
  113. protected $headers = array();
  114. /**
  115. * The HTTP response body
  116. *
  117. * @var string
  118. */
  119. protected $body;
  120. /**
  121. * HTTP response constructor
  122. *
  123. * In most cases, you would use Zend_Http_Response::fromString to parse an HTTP
  124. * response string and create a new Zend_Http_Response object.
  125. *
  126. * NOTE: The constructor no longer accepts nulls or empty values for the code and
  127. * headers and will throw an exception if the passed values do not form a valid HTTP
  128. * responses.
  129. *
  130. * If no message is passed, the message will be guessed according to the response code.
  131. *
  132. * @param int $code Response code (200, 404, ...)
  133. * @param array $headers Headers array
  134. * @param string $body Response body
  135. * @param string $version HTTP version
  136. * @param string $message Response code as text
  137. * @throws Zend_Http_Exception
  138. */
  139. public function __construct($code, array $headers, $body = null, $version = '1.1', $message = null)
  140. {
  141. // Make sure the response code is valid and set it
  142. if (self::responseCodeAsText($code) === null) {
  143. #require_once 'Zend/Http/Exception.php';
  144. throw new Zend_Http_Exception("{$code} is not a valid HTTP response code");
  145. }
  146. $this->code = $code;
  147. foreach ($headers as $name => $value) {
  148. if (is_int($name)) {
  149. $header = explode(":", $value, 2);
  150. if (count($header) != 2) {
  151. #require_once 'Zend/Http/Exception.php';
  152. throw new Zend_Http_Exception("'{$value}' is not a valid HTTP header");
  153. }
  154. $name = trim($header[0]);
  155. $value = trim($header[1]);
  156. }
  157. $this->headers[ucwords(strtolower($name))] = $value;
  158. }
  159. // Set the body
  160. $this->body = $body;
  161. // Set the HTTP version
  162. if (! preg_match('|^\d\.\d$|', $version)) {
  163. #require_once 'Zend/Http/Exception.php';
  164. throw new Zend_Http_Exception("Invalid HTTP response version: $version");
  165. }
  166. $this->version = $version;
  167. // If we got the response message, set it. Else, set it according to
  168. // the response code
  169. if (is_string($message)) {
  170. $this->message = $message;
  171. } else {
  172. $this->message = self::responseCodeAsText($code);
  173. }
  174. }
  175. /**
  176. * Check whether the response is an error
  177. *
  178. * @return boolean
  179. */
  180. public function isError()
  181. {
  182. $restype = floor($this->code / 100);
  183. if ($restype == 4 || $restype == 5) {
  184. return true;
  185. }
  186. return false;
  187. }
  188. /**
  189. * Check whether the response in successful
  190. *
  191. * @return boolean
  192. */
  193. public function isSuccessful()
  194. {
  195. $restype = floor($this->code / 100);
  196. if ($restype == 2 || $restype == 1) { // Shouldn't 3xx count as success as well ???
  197. return true;
  198. }
  199. return false;
  200. }
  201. /**
  202. * Check whether the response is a redirection
  203. *
  204. * @return boolean
  205. */
  206. public function isRedirect()
  207. {
  208. $restype = floor($this->code / 100);
  209. if ($restype == 3) {
  210. return true;
  211. }
  212. return false;
  213. }
  214. /**
  215. * Get the response body as string
  216. *
  217. * This method returns the body of the HTTP response (the content), as it
  218. * should be in it's readable version - that is, after decoding it (if it
  219. * was decoded), deflating it (if it was gzip compressed), etc.
  220. *
  221. * If you want to get the raw body (as transfered on wire) use
  222. * $this->getRawBody() instead.
  223. *
  224. * @return string
  225. */
  226. public function getBody()
  227. {
  228. $body = '';
  229. // Decode the body if it was transfer-encoded
  230. switch (strtolower($this->getHeader('transfer-encoding'))) {
  231. // Handle chunked body
  232. case 'chunked':
  233. $body = self::decodeChunkedBody($this->body);
  234. break;
  235. // No transfer encoding, or unknown encoding extension:
  236. // return body as is
  237. default:
  238. $body = $this->body;
  239. break;
  240. }
  241. // Decode any content-encoding (gzip or deflate) if needed
  242. switch (strtolower($this->getHeader('content-encoding'))) {
  243. // Handle gzip encoding
  244. case 'gzip':
  245. $body = self::decodeGzip($body);
  246. break;
  247. // Handle deflate encoding
  248. case 'deflate':
  249. $body = self::decodeDeflate($body);
  250. break;
  251. default:
  252. break;
  253. }
  254. return $body;
  255. }
  256. /**
  257. * Get the raw response body (as transfered "on wire") as string
  258. *
  259. * If the body is encoded (with Transfer-Encoding, not content-encoding -
  260. * IE "chunked" body), gzip compressed, etc. it will not be decoded.
  261. *
  262. * @return string
  263. */
  264. public function getRawBody()
  265. {
  266. return $this->body;
  267. }
  268. /**
  269. * Get the HTTP version of the response
  270. *
  271. * @return string
  272. */
  273. public function getVersion()
  274. {
  275. return $this->version;
  276. }
  277. /**
  278. * Get the HTTP response status code
  279. *
  280. * @return int
  281. */
  282. public function getStatus()
  283. {
  284. return $this->code;
  285. }
  286. /**
  287. * Return a message describing the HTTP response code
  288. * (Eg. "OK", "Not Found", "Moved Permanently")
  289. *
  290. * @return string
  291. */
  292. public function getMessage()
  293. {
  294. return $this->message;
  295. }
  296. /**
  297. * Get the response headers
  298. *
  299. * @return array
  300. */
  301. public function getHeaders()
  302. {
  303. return $this->headers;
  304. }
  305. /**
  306. * Get a specific header as string, or null if it is not set
  307. *
  308. * @param string$header
  309. * @return string|array|null
  310. */
  311. public function getHeader($header)
  312. {
  313. $header = ucwords(strtolower($header));
  314. if (! is_string($header) || ! isset($this->headers[$header])) return null;
  315. return $this->headers[$header];
  316. }
  317. /**
  318. * Get all headers as string
  319. *
  320. * @param boolean $status_line Whether to return the first status line (IE "HTTP 200 OK")
  321. * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
  322. * @return string
  323. */
  324. public function getHeadersAsString($status_line = true, $br = "\n")
  325. {
  326. $str = '';
  327. if ($status_line) {
  328. $str = "HTTP/{$this->version} {$this->code} {$this->message}{$br}";
  329. }
  330. // Iterate over the headers and stringify them
  331. foreach ($this->headers as $name => $value)
  332. {
  333. if (is_string($value))
  334. $str .= "{$name}: {$value}{$br}";
  335. elseif (is_array($value)) {
  336. foreach ($value as $subval) {
  337. $str .= "{$name}: {$subval}{$br}";
  338. }
  339. }
  340. }
  341. return $str;
  342. }
  343. /**
  344. * Get the entire response as string
  345. *
  346. * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
  347. * @return string
  348. */
  349. public function asString($br = "\n")
  350. {
  351. return $this->getHeadersAsString(true, $br) . $br . $this->getRawBody();
  352. }
  353. /**
  354. * Implements magic __toString()
  355. *
  356. * @return string
  357. */
  358. public function __toString()
  359. {
  360. return $this->asString();
  361. }
  362. /**
  363. * A convenience function that returns a text representation of
  364. * HTTP response codes. Returns 'Unknown' for unknown codes.
  365. * Returns array of all codes, if $code is not specified.
  366. *
  367. * Conforms to HTTP/1.1 as defined in RFC 2616 (except for 'Unknown')
  368. * See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 for reference
  369. *
  370. * @param int $code HTTP response code
  371. * @param boolean $http11 Use HTTP version 1.1
  372. * @return string
  373. */
  374. public static function responseCodeAsText($code = null, $http11 = true)
  375. {
  376. $messages = self::$messages;
  377. if (! $http11) $messages[302] = 'Moved Temporarily';
  378. if ($code === null) {
  379. return $messages;
  380. } elseif (isset($messages[$code])) {
  381. return $messages[$code];
  382. } else {
  383. return 'Unknown';
  384. }
  385. }
  386. /**
  387. * Extract the response code from a response string
  388. *
  389. * @param string $response_str
  390. * @return int
  391. */
  392. public static function extractCode($response_str)
  393. {
  394. preg_match("|^HTTP/[\d\.x]+ (\d+)|", $response_str, $m);
  395. if (isset($m[1])) {
  396. return (int) $m[1];
  397. } else {
  398. return false;
  399. }
  400. }
  401. /**
  402. * Extract the HTTP message from a response
  403. *
  404. * @param string $response_str
  405. * @return string
  406. */
  407. public static function extractMessage($response_str)
  408. {
  409. preg_match("|^HTTP/[\d\.x]+ \d+ ([^\r\n]+)|", $response_str, $m);
  410. if (isset($m[1])) {
  411. return $m[1];
  412. } else {
  413. return false;
  414. }
  415. }
  416. /**
  417. * Extract the HTTP version from a response
  418. *
  419. * @param string $response_str
  420. * @return string
  421. */
  422. public static function extractVersion($response_str)
  423. {
  424. preg_match("|^HTTP/([\d\.x]+) \d+|", $response_str, $m);
  425. if (isset($m[1])) {
  426. return $m[1];
  427. } else {
  428. return false;
  429. }
  430. }
  431. /**
  432. * Extract the headers from a response string
  433. *
  434. * @param string $response_str
  435. * @return array
  436. */
  437. public static function extractHeaders($response_str)
  438. {
  439. $headers = array();
  440. // First, split body and headers
  441. $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2);
  442. if (! $parts[0]) return $headers;
  443. // Split headers part to lines
  444. $lines = explode("\n", $parts[0]);
  445. unset($parts);
  446. $last_header = null;
  447. foreach($lines as $line) {
  448. $line = trim($line, "\r\n");
  449. if ($line == "") break;
  450. // Locate headers like 'Location: ...' and 'Location:...' (note the missing space)
  451. if (preg_match("|^([\w-]+):\s*(.+)|", $line, $m)) {
  452. unset($last_header);
  453. $h_name = strtolower($m[1]);
  454. $h_value = $m[2];
  455. if (isset($headers[$h_name])) {
  456. if (! is_array($headers[$h_name])) {
  457. $headers[$h_name] = array($headers[$h_name]);
  458. }
  459. $headers[$h_name][] = $h_value;
  460. } else {
  461. $headers[$h_name] = $h_value;
  462. }
  463. $last_header = $h_name;
  464. } elseif (preg_match("|^\s+(.+)$|", $line, $m) && $last_header !== null) {
  465. if (is_array($headers[$last_header])) {
  466. end($headers[$last_header]);
  467. $last_header_key = key($headers[$last_header]);
  468. $headers[$last_header][$last_header_key] .= $m[1];
  469. } else {
  470. $headers[$last_header] .= $m[1];
  471. }
  472. }
  473. }
  474. return $headers;
  475. }
  476. /**
  477. * Extract the body from a response string
  478. *
  479. * @param string $response_str
  480. * @return string
  481. */
  482. public static function extractBody($response_str)
  483. {
  484. $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2);
  485. if (isset($parts[1])) {
  486. return $parts[1];
  487. }
  488. return '';
  489. }
  490. /**
  491. * Decode a "chunked" transfer-encoded body and return the decoded text
  492. *
  493. * @param string $body
  494. * @return string
  495. */
  496. public static function decodeChunkedBody($body)
  497. {
  498. $decBody = '';
  499. // If mbstring overloads substr and strlen functions, we have to
  500. // override it's internal encoding
  501. if (function_exists('mb_internal_encoding') &&
  502. ((int) ini_get('mbstring.func_overload')) & 2) {
  503. $mbIntEnc = mb_internal_encoding();
  504. mb_internal_encoding('ASCII');
  505. }
  506. while (trim($body)) {
  507. if (! preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm", $body, $m)) {
  508. #require_once 'Zend/Http/Exception.php';
  509. throw new Zend_Http_Exception("Error parsing body - doesn't seem to be a chunked message");
  510. }
  511. $length = hexdec(trim($m[1]));
  512. $cut = strlen($m[0]);
  513. $decBody .= substr($body, $cut, $length);
  514. $body = substr($body, $cut + $length + 2);
  515. }
  516. if (isset($mbIntEnc)) {
  517. mb_internal_encoding($mbIntEnc);
  518. }
  519. return $decBody;
  520. }
  521. /**
  522. * Decode a gzip encoded message (when Content-encoding = gzip)
  523. *
  524. * Currently requires PHP with zlib support
  525. *
  526. * @param string $body
  527. * @return string
  528. */
  529. public static function decodeGzip($body)
  530. {
  531. if (! function_exists('gzinflate')) {
  532. #require_once 'Zend/Http/Exception.php';
  533. throw new Zend_Http_Exception(
  534. 'zlib extension is required in order to decode "gzip" encoding'
  535. );
  536. }
  537. return gzinflate(substr($body, 10));
  538. }
  539. /**
  540. * Decode a zlib deflated message (when Content-encoding = deflate)
  541. *
  542. * Currently requires PHP with zlib support
  543. *
  544. * @param string $body
  545. * @return string
  546. */
  547. public static function decodeDeflate($body)
  548. {
  549. if (! function_exists('gzuncompress')) {
  550. #require_once 'Zend/Http/Exception.php';
  551. throw new Zend_Http_Exception(
  552. 'zlib extension is required in order to decode "deflate" encoding'
  553. );
  554. }
  555. /**
  556. * Some servers (IIS ?) send a broken deflate response, without the
  557. * RFC-required zlib header.
  558. *
  559. * We try to detect the zlib header, and if it does not exsit we
  560. * teat the body is plain DEFLATE content.
  561. *
  562. * This method was adapted from PEAR HTTP_Request2 by (c) Alexey Borzov
  563. *
  564. * @link http://framework.zend.com/issues/browse/ZF-6040
  565. */
  566. $zlibHeader = unpack('n', substr($body, 0, 2));
  567. if ($zlibHeader[1] % 31 == 0) {
  568. return gzuncompress($body);
  569. } else {
  570. return gzinflate($body);
  571. }
  572. }
  573. /**
  574. * Create a new Zend_Http_Response object from a string
  575. *
  576. * @param string $response_str
  577. * @return Zend_Http_Response
  578. */
  579. public static function fromString($response_str)
  580. {
  581. $code = self::extractCode($response_str);
  582. $headers = self::extractHeaders($response_str);
  583. $body = self::extractBody($response_str);
  584. $version = self::extractVersion($response_str);
  585. $message = self::extractMessage($response_str);
  586. return new Zend_Http_Response($code, $headers, $body, $version, $message);
  587. }
  588. }