PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/standard/branches/pdo_ibm_ids_support/library/Zend/Http/Client/Adapter/Socket.php

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