PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/php/external/Zend/Http/Client/Adapter/Socket.php

https://github.com/anirvan/shindig-profiles
PHP | 294 lines | 147 code | 39 blank | 108 comment | 50 complexity | 811a17d6fd46b205cda3ac216bd95cc5 MD5 | raw file
Possible License(s): Apache-2.0
  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 Client_Adapter
  18. * @version $Id: Socket.php 8064 2008-02-16 10:58:39Z thomas $
  19. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  20. * @license http://framework.zend.com/license/new-bsd New BSD License
  21. */
  22. require_once 'external/Zend/Uri/Http.php';
  23. require_once 'external/Zend/Http/Client/Adapter/Interface.php';
  24. /**
  25. * A sockets based (stream_socket_client) adapter class for Zend_Http_Client. Can be used
  26. * on almost every PHP environment, and does not require any special extensions.
  27. *
  28. * @category Zend
  29. * @package Zend_Http
  30. * @subpackage Client_Adapter
  31. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  32. * @license http://framework.zend.com/license/new-bsd New BSD License
  33. */
  34. class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface {
  35. /**
  36. * The socket for server connection
  37. *
  38. * @var resource|null
  39. */
  40. protected $socket = null;
  41. /**
  42. * What host/port are we connected to?
  43. *
  44. * @var array
  45. */
  46. protected $connected_to = array(null, null);
  47. /**
  48. * Parameters array
  49. *
  50. * @var array
  51. */
  52. protected $config = array('ssltransport' => 'ssl', 'sslcert' => null, 'sslpassphrase' => null);
  53. /**
  54. * Request method - will be set by write() and might be used by read()
  55. *
  56. * @var string
  57. */
  58. protected $method = null;
  59. /**
  60. * Adapter constructor, currently empty. Config is set using setConfig()
  61. *
  62. */
  63. public function __construct() {}
  64. /**
  65. * Set the configuration array for the adapter
  66. *
  67. * @param array $config
  68. */
  69. public function setConfig($config = array()) {
  70. if (! is_array($config)) {
  71. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  72. throw new Zend_Http_Client_Adapter_Exception('$config expects an array, ' . gettype($config) . ' recieved.');
  73. }
  74. foreach ($config as $k => $v) {
  75. $this->config[strtolower($k)] = $v;
  76. }
  77. }
  78. /**
  79. * Connect to the remote server
  80. *
  81. * @param string $host
  82. * @param int $port
  83. * @param boolean $secure
  84. * @param int $timeout
  85. */
  86. public function connect($host, $port = 80, $secure = false) {
  87. // If the URI should be accessed via SSL, prepend the Hostname with ssl://
  88. $host = ($secure ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
  89. // If we are connected to the wrong host, disconnect first
  90. if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) {
  91. if (is_resource($this->socket)) $this->close();
  92. }
  93. // Now, if we are not connected, connect
  94. if (! is_resource($this->socket) || ! $this->config['keepalive']) {
  95. $context = stream_context_create();
  96. if ($secure) {
  97. if ($this->config['sslcert'] !== null) {
  98. if (! stream_context_set_option($context, 'ssl', 'local_cert', $this->config['sslcert'])) {
  99. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  100. throw new Zend_Http_Client_Adapter_Exception('Unable to set sslcert option');
  101. }
  102. }
  103. if ($this->config['sslpassphrase'] !== null) {
  104. if (! stream_context_set_option($context, 'ssl', 'passphrase', $this->config['sslpassphrase'])) {
  105. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  106. throw new Zend_Http_Client_Adapter_Exception('Unable to set sslpassphrase option');
  107. }
  108. }
  109. }
  110. $this->socket = @stream_socket_client($host . ':' . $port, $errno, $errstr, (int)$this->config['timeout'], STREAM_CLIENT_CONNECT, $context);
  111. if (! $this->socket) {
  112. $this->close();
  113. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  114. throw new Zend_Http_Client_Adapter_Exception('Unable to Connect to ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr);
  115. }
  116. // Set the stream timeout
  117. if (! stream_set_timeout($this->socket, (int)$this->config['timeout'])) {
  118. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  119. throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout');
  120. }
  121. // Update connected_to
  122. $this->connected_to = array($host, $port);
  123. }
  124. }
  125. /**
  126. * Send request to the remote server
  127. *
  128. * @param string $method
  129. * @param Zend_Uri_Http $uri
  130. * @param string $http_ver
  131. * @param array $headers
  132. * @param string $body
  133. * @return string Request as string
  134. */
  135. public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') {
  136. // Make sure we're properly connected
  137. if (! $this->socket) {
  138. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  139. throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected');
  140. }
  141. $host = $uri->getHost();
  142. $host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
  143. if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) {
  144. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  145. throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong host');
  146. }
  147. // Save request method for later
  148. $this->method = $method;
  149. // Build request headers
  150. $path = $uri->getPath();
  151. if ($uri->getQuery()) $path .= '?' . $uri->getQuery();
  152. $request = "{$method} {$path} HTTP/{$http_ver}\r\n";
  153. foreach ($headers as $k => $v) {
  154. if (is_string($k)) $v = ucfirst($k) . ": $v";
  155. $request .= "$v\r\n";
  156. }
  157. // Add the request body
  158. $request .= "\r\n" . $body;
  159. // Send the request
  160. if (! @fwrite($this->socket, $request)) {
  161. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  162. throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
  163. }
  164. return $request;
  165. }
  166. /**
  167. * Read response from server
  168. *
  169. * @return string
  170. */
  171. public function read() {
  172. // First, read headers only
  173. $response = '';
  174. $gotStatus = false;
  175. while ($line = @fgets($this->socket)) {
  176. $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
  177. if ($gotStatus) {
  178. $response .= $line;
  179. if (! chop($line)) break;
  180. }
  181. }
  182. // Handle 100 and 101 responses internally by restarting the read again
  183. if (Zend_Http_Response::extractCode($response) == 100 || Zend_Http_Response::extractCode($response) == 101) return $this->read();
  184. // If this was a HEAD request, return after reading the header (no need to read body)
  185. if ($this->method == Zend_Http_Client::HEAD) return $response;
  186. // Check headers to see what kind of connection / transfer encoding we have
  187. $headers = Zend_Http_Response::extractHeaders($response);
  188. // if the connection is set to close, just read until socket closes
  189. if (isset($headers['connection']) && $headers['connection'] == 'close') {
  190. while ($buff = @fread($this->socket, 8192)) {
  191. $response .= $buff;
  192. }
  193. $this->close();
  194. // Else, if we got a transfer-encoding header (chunked body)
  195. } elseif (isset($headers['transfer-encoding'])) {
  196. if ($headers['transfer-encoding'] == 'chunked') {
  197. do {
  198. $chunk = '';
  199. $line = @fgets($this->socket);
  200. $chunk .= $line;
  201. $hexchunksize = ltrim(chop($line), '0');
  202. $hexchunksize = strlen($hexchunksize) ? strtolower($hexchunksize) : 0;
  203. $chunksize = hexdec(chop($line));
  204. if (dechex($chunksize) != $hexchunksize) {
  205. @fclose($this->socket);
  206. require_once 'external/Zend/Http/Client/Adapter/Exception.php';
  207. throw new Zend_Http_Client_Adapter_Exception('Invalid chunk size "' . $hexchunksize . '" unable to read chunked body');
  208. }
  209. $left_to_read = $chunksize;
  210. while ($left_to_read > 0) {
  211. $line = @fread($this->socket, $left_to_read);
  212. $chunk .= $line;
  213. $left_to_read -= strlen($line);
  214. }
  215. $chunk .= @fgets($this->socket);
  216. $response .= $chunk;
  217. } while ($chunksize > 0);
  218. } else {
  219. throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' . $headers['transfer-encoding'] . '" transfer encoding');
  220. }
  221. // Else, if we got the content-length header, read this number of bytes
  222. } elseif (isset($headers['content-length'])) {
  223. $left_to_read = $headers['content-length'];
  224. $chunk = '';
  225. while ($left_to_read > 0) {
  226. $chunk = @fread($this->socket, $left_to_read);
  227. $left_to_read -= strlen($chunk);
  228. $response .= $chunk;
  229. }
  230. // Fallback: just read the response (should not happen)
  231. } else {
  232. while ($buff = @fread($this->socket, 8192)) {
  233. $response .= $buff;
  234. }
  235. $this->close();
  236. }
  237. return $response;
  238. }
  239. /**
  240. * Close the connection to the server
  241. *
  242. */
  243. public function close() {
  244. if (is_resource($this->socket)) @fclose($this->socket);
  245. $this->socket = null;
  246. $this->connected_to = array(null, null);
  247. }
  248. /**
  249. * Destructor: make sure the socket is disconnected
  250. *
  251. */
  252. public function __destruct() {
  253. if ($this->socket) $this->close();
  254. }
  255. }