PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ZendGdata-1.8.4PL1/library/Zend/Http/Client/Adapter/Socket.php

https://github.com/dmolsen/MIT-Mobile-Web
PHP | 353 lines | 192 code | 43 blank | 118 comment | 65 complexity | 4a8d69f3e4e567f660a6ac09889c050f MD5 | raw file
  1. <?
  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 13013 2008-12-04 12:04:24Z yoshida@zend.co.jp $
  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 'Zend/Uri/Http.php';
  23. require_once '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. /**
  37. * The socket for server connection
  38. *
  39. * @var resource|null
  40. */
  41. protected $socket = null;
  42. /**
  43. * What host/port are we connected to?
  44. *
  45. * @var array
  46. */
  47. protected $connected_to = array(null, null);
  48. /**
  49. * Parameters array
  50. *
  51. * @var array
  52. */
  53. protected $config = array(
  54. 'persistent' => false,
  55. 'ssltransport' => 'ssl',
  56. 'sslcert' => null,
  57. 'sslpassphrase' => null
  58. );
  59. /**
  60. * Request method - will be set by write() and might be used by read()
  61. *
  62. * @var string
  63. */
  64. protected $method = null;
  65. /**
  66. * Adapter constructor, currently empty. Config is set using setConfig()
  67. *
  68. */
  69. public function __construct()
  70. {
  71. }
  72. /**
  73. * Set the configuration array for the adapter
  74. *
  75. * @param array $config
  76. */
  77. public function setConfig($config = array())
  78. {
  79. if (! is_array($config)) {
  80. require_once 'Zend/Http/Client/Adapter/Exception.php';
  81. throw new Zend_Http_Client_Adapter_Exception(
  82. '$concig expects an array, ' . gettype($config) . ' recieved.');
  83. }
  84. foreach ($config as $k => $v) {
  85. $this->config[strtolower($k)] = $v;
  86. }
  87. }
  88. /**
  89. * Connect to the remote server
  90. *
  91. * @param string $host
  92. * @param int $port
  93. * @param boolean $secure
  94. * @param int $timeout
  95. */
  96. public function connect($host, $port = 80, $secure = false)
  97. {
  98. // If the URI should be accessed via SSL, prepend the Hostname with ssl://
  99. $host = ($secure ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
  100. // If we are connected to the wrong host, disconnect first
  101. if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) {
  102. if (is_resource($this->socket)) $this->close();
  103. }
  104. // Now, if we are not connected, connect
  105. if (! is_resource($this->socket) || ! $this->config['keepalive']) {
  106. $context = stream_context_create();
  107. if ($secure) {
  108. if ($this->config['sslcert'] !== null) {
  109. if (! stream_context_set_option($context, 'ssl', 'local_cert',
  110. $this->config['sslcert'])) {
  111. require_once 'Zend/Http/Client/Adapter/Exception.php';
  112. throw new Zend_Http_Client_Adapter_Exception('Unable to set sslcert option');
  113. }
  114. }
  115. if ($this->config['sslpassphrase'] !== null) {
  116. if (! stream_context_set_option($context, 'ssl', 'passphrase',
  117. $this->config['sslpassphrase'])) {
  118. require_once 'Zend/Http/Client/Adapter/Exception.php';
  119. throw new Zend_Http_Client_Adapter_Exception('Unable to set sslpassphrase option');
  120. }
  121. }
  122. }
  123. $flags = STREAM_CLIENT_CONNECT;
  124. if ($this->config['persistent']) $flags |= STREAM_CLIENT_PERSISTENT;
  125. $this->socket = @stream_socket_client($host . ':' . $port,
  126. $errno,
  127. $errstr,
  128. (int) $this->config['timeout'],
  129. $flags,
  130. $context);
  131. if (! $this->socket) {
  132. $this->close();
  133. require_once 'Zend/Http/Client/Adapter/Exception.php';
  134. throw new Zend_Http_Client_Adapter_Exception(
  135. 'Unable to Connect to ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr);
  136. }
  137. // Set the stream timeout
  138. if (! stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
  139. require_once 'Zend/Http/Client/Adapter/Exception.php';
  140. throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout');
  141. }
  142. // Update connected_to
  143. $this->connected_to = array($host, $port);
  144. }
  145. }
  146. /**
  147. * Send request to the remote server
  148. *
  149. * @param string $method
  150. * @param Zend_Uri_Http $uri
  151. * @param string $http_ver
  152. * @param array $headers
  153. * @param string $body
  154. * @return string Request as string
  155. */
  156. public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
  157. {
  158. // Make sure we're properly connected
  159. if (! $this->socket) {
  160. require_once 'Zend/Http/Client/Adapter/Exception.php';
  161. throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected');
  162. }
  163. $host = $uri->getHost();
  164. $host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
  165. if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) {
  166. require_once 'Zend/Http/Client/Adapter/Exception.php';
  167. throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong host');
  168. }
  169. // Save request method for later
  170. $this->method = $method;
  171. // Build request headers
  172. $path = $uri->getPath();
  173. if ($uri->getQuery()) $path .= '?' . $uri->getQuery();
  174. $request = "{$method} {$path} HTTP/{$http_ver}\r\n";
  175. foreach ($headers as $k => $v) {
  176. if (is_string($k)) $v = ucfirst($k) . ": $v";
  177. $request .= "$v\r\n";
  178. }
  179. // Add the request body
  180. $request .= "\r\n" . $body;
  181. // Send the request
  182. if (! @fwrite($this->socket, $request)) {
  183. require_once 'Zend/Http/Client/Adapter/Exception.php';
  184. throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
  185. }
  186. return $request;
  187. }
  188. /**
  189. * Read response from server
  190. *
  191. * @return string
  192. */
  193. public function read()
  194. {
  195. // First, read headers only
  196. $response = '';
  197. $gotStatus = false;
  198. while (($line = @fgets($this->socket)) !== false) {
  199. $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
  200. if ($gotStatus) {
  201. $response .= $line;
  202. if (rtrim($line) === '') break;
  203. }
  204. }
  205. $statusCode = Zend_Http_Response::extractCode($response);
  206. // Handle 100 and 101 responses internally by restarting the read again
  207. if ($statusCode == 100 || $statusCode == 101) return $this->read();
  208. /**
  209. * Responses to HEAD requests and 204 or 304 responses are not expected
  210. * to have a body - stop reading here
  211. */
  212. if ($statusCode == 304 || $statusCode == 204 ||
  213. $this->method == Zend_Http_Client::HEAD) return $response;
  214. // Check headers to see what kind of connection / transfer encoding we have
  215. $headers = Zend_Http_Response::extractHeaders($response);
  216. // If we got a 'transfer-encoding: chunked' header
  217. if (isset($headers['transfer-encoding'])) {
  218. if ($headers['transfer-encoding'] == 'chunked') {
  219. do {
  220. $line = @fgets($this->socket);
  221. $chunk = $line;
  222. // Figure out the next chunk size
  223. $chunksize = trim($line);
  224. if (! ctype_xdigit($chunksize)) {
  225. $this->close();
  226. require_once 'Zend/Http/Client/Adapter/Exception.php';
  227. throw new Zend_Http_Client_Adapter_Exception('Invalid chunk size "' .
  228. $chunksize . '" unable to read chunked body');
  229. }
  230. // Convert the hexadecimal value to plain integer
  231. $chunksize = hexdec($chunksize);
  232. // Read chunk
  233. $left_to_read = $chunksize;
  234. while ($left_to_read > 0) {
  235. $line = @fread($this->socket, $left_to_read);
  236. if ($line === false || strlen($line) === 0)
  237. {
  238. break;
  239. } else {
  240. $chunk .= $line;
  241. $left_to_read -= strlen($line);
  242. }
  243. // Break if the connection ended prematurely
  244. if (feof($this->socket)) break;
  245. }
  246. $chunk .= @fgets($this->socket);
  247. $response .= $chunk;
  248. } while ($chunksize > 0);
  249. } else {
  250. throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' .
  251. $headers['transfer-encoding'] . '" transfer encoding');
  252. }
  253. // Else, if we got the content-length header, read this number of bytes
  254. } elseif (isset($headers['content-length'])) {
  255. $left_to_read = $headers['content-length'];
  256. $chunk = '';
  257. while ($left_to_read > 0) {
  258. $chunk = @fread($this->socket, $left_to_read);
  259. if ($chunk === false || strlen($chunk) === 0)
  260. {
  261. break;
  262. } else {
  263. $left_to_read -= strlen($chunk);
  264. $response .= $chunk;
  265. }
  266. // Break if the connection ended prematurely
  267. if (feof($this->socket)) break;
  268. }
  269. // Fallback: just read the response until EOF
  270. } else {
  271. do {
  272. $buff = @fread($this->socket, 8192);
  273. if ($buff === false || strlen($buff) === 0)
  274. {
  275. break;
  276. } else {
  277. $response .= $buff;
  278. }
  279. } while (feof($this->socket) === false);
  280. $this->close();
  281. }
  282. // Close the connection if requested to do so by the server
  283. if (isset($headers['connection']) && $headers['connection'] == 'close') {
  284. $this->close();
  285. }
  286. return $response;
  287. }
  288. /**
  289. * Close the connection to the server
  290. *
  291. */
  292. public function close()
  293. {
  294. if (is_resource($this->socket)) @fclose($this->socket);
  295. $this->socket = null;
  296. $this->connected_to = array(null, null);
  297. }
  298. /**
  299. * Destructor: make sure the socket is disconnected
  300. *
  301. * If we are in persistent TCP mode, will not close the connection
  302. *
  303. */
  304. public function __destruct()
  305. {
  306. if (! $this->config['persistent']) {
  307. if ($this->socket) $this->close();
  308. }
  309. }
  310. }