PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/http/transport/socket.php

https://bitbucket.org/organicdevelopment/joomla-2.5
PHP | 253 lines | 124 code | 34 blank | 95 comment | 18 complexity | 6096a2440bcd92604b5f60b141c9c7dd MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, MIT, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage HTTP
  5. *
  6. * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die();
  10. /**
  11. * HTTP transport class for using sockets directly.
  12. *
  13. * @package Joomla.Platform
  14. * @subpackage HTTP
  15. * @since 11.3
  16. */
  17. class JHttpTransportSocket implements JHttpTransport
  18. {
  19. /**
  20. * @var array Reusable socket connections.
  21. * @since 11.3
  22. */
  23. protected $connections;
  24. /**
  25. * @var JRegistry The client options.
  26. * @since 11.3
  27. */
  28. protected $options;
  29. /**
  30. * Constructor.
  31. *
  32. * @param JRegistry &$options Client options object.
  33. *
  34. * @since 11.3
  35. * @throws RuntimeException
  36. */
  37. public function __construct(JRegistry &$options)
  38. {
  39. if (!function_exists('fsockopen') || !is_callable('fsockopen'))
  40. {
  41. throw new RuntimeException('Cannot use a socket transport when fsockopen() is not available.');
  42. }
  43. $this->options = $options;
  44. }
  45. /**
  46. * Send a request to the server and return a JHttpResponse object with the response.
  47. *
  48. * @param string $method The HTTP method for sending the request.
  49. * @param JUri $uri The URI to the resource to request.
  50. * @param mixed $data Either an associative array or a string to be sent with the request.
  51. * @param array $headers An array of request headers to send with the request.
  52. * @param integer $timeout Read timeout in seconds.
  53. * @param string $userAgent The optional user agent string to send with the request.
  54. *
  55. * @return JHttpResponse
  56. *
  57. * @since 11.3
  58. * @throws RuntimeException
  59. */
  60. public function request($method, JUri $uri, $data = null, array $headers = null, $timeout = null, $userAgent = null)
  61. {
  62. $connection = $this->connect($uri, $timeout);
  63. // Make sure the connection is alive and valid.
  64. if (is_resource($connection))
  65. {
  66. // Make sure the connection has not timed out.
  67. $meta = stream_get_meta_data($connection);
  68. if ($meta['timed_out'])
  69. {
  70. throw new RuntimeException('Server connection timed out.');
  71. }
  72. }
  73. else
  74. {
  75. throw new RuntimeException('Not connected to server.');
  76. }
  77. // Get the request path from the URI object.
  78. $path = $uri->toString(array('path', 'query'));
  79. // If we have data to send make sure our request is setup for it.
  80. if (!empty($data))
  81. {
  82. // If the data is not a scalar value encode it to be sent with the request.
  83. if (!is_scalar($data))
  84. {
  85. $data = http_build_query($data);
  86. }
  87. // Add the relevant headers.
  88. $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  89. $headers['Content-Length'] = strlen($data);
  90. }
  91. // Build the request payload.
  92. $request = array();
  93. $request[] = strtoupper($method) . ' ' . ((empty($path)) ? '/' : $path) . ' HTTP/1.0';
  94. $request[] = 'Host: ' . $uri->getHost();
  95. // If an explicit user agent is given use it.
  96. if (isset($userAgent))
  97. {
  98. $headers['User-Agent'] = $userAgent;
  99. }
  100. // If there are custom headers to send add them to the request payload.
  101. if (is_array($headers))
  102. {
  103. foreach ($headers as $k => $v)
  104. {
  105. $request[] = $k . ': ' . $v;
  106. }
  107. }
  108. // If we have data to send add it to the request payload.
  109. if (!empty($data))
  110. {
  111. $request[] = null;
  112. $request[] = $data;
  113. }
  114. // Send the request to the server.
  115. fwrite($connection, implode("\r\n", $request) . "\r\n\r\n");
  116. // Get the response data from the server.
  117. $content = '';
  118. while (!feof($connection))
  119. {
  120. $content .= fgets($connection, 4096);
  121. }
  122. return $this->getResponse($content);
  123. }
  124. /**
  125. * Method to get a response object from a server response.
  126. *
  127. * @param string $content The complete server response, including headers.
  128. *
  129. * @return JHttpResponse
  130. *
  131. * @since 11.3
  132. * @throws UnexpectedValueException
  133. */
  134. protected function getResponse($content)
  135. {
  136. // Create the response object.
  137. $return = new JHttpResponse;
  138. // Split the response into headers and body.
  139. $response = explode("\r\n\r\n", $content, 2);
  140. // Get the response headers as an array.
  141. $headers = explode("\r\n", $response[0]);
  142. // Set the body for the response.
  143. $return->body = $response[1];
  144. // Get the response code from the first offset of the response headers.
  145. preg_match('/[0-9]{3}/', array_shift($headers), $matches);
  146. $code = $matches[0];
  147. if (is_numeric($code))
  148. {
  149. $return->code = (int) $code;
  150. }
  151. // No valid response code was detected.
  152. else
  153. {
  154. throw new UnexpectedValueException('No HTTP response code found.');
  155. }
  156. // Add the response headers to the response object.
  157. foreach ($headers as $header)
  158. {
  159. $pos = strpos($header, ':');
  160. $return->headers[trim(substr($header, 0, $pos))] = trim(substr($header, ($pos + 1)));
  161. }
  162. return $return;
  163. }
  164. /**
  165. * Method to connect to a server and get the resource.
  166. *
  167. * @param JUri $uri The URI to connect with.
  168. * @param integer $timeout Read timeout in seconds.
  169. *
  170. * @return resource Socket connection resource.
  171. *
  172. * @since 11.3
  173. * @throws RuntimeException
  174. */
  175. protected function connect(JUri $uri, $timeout = null)
  176. {
  177. // Initialize variables.
  178. $errno = null;
  179. $err = null;
  180. // Get the host from the uri.
  181. $host = ($uri->isSSL()) ? 'ssl://' . $uri->getHost() : $uri->getHost();
  182. // If the port is not explicitly set in the URI detect it.
  183. if (!$uri->getPort())
  184. {
  185. $port = ($uri->getScheme() == 'https') ? 443 : 80;
  186. }
  187. // Use the set port.
  188. else
  189. {
  190. $port = $uri->getPort();
  191. }
  192. // Build the connection key for resource memory caching.
  193. $key = md5($host . $port);
  194. // If the connection already exists, use it.
  195. if (!empty($this->connections[$key]) && is_resource($this->connections[$key]))
  196. {
  197. // Make sure the connection has not timed out.
  198. $meta = stream_get_meta_data($this->connections[$key]);
  199. if (!$meta['timed_out'])
  200. {
  201. return $this->connections[$key];
  202. }
  203. }
  204. // Attempt to connect to the server.
  205. $connection = fsockopen($host, $port, $errno, $err, $timeout);
  206. if (!$connection)
  207. {
  208. throw new RuntimeException($err, $errno);
  209. }
  210. // Since the connection was successful let's store it in case we need to use it later.
  211. $this->connections[$key] = $connection;
  212. // If an explicit timeout is set, set it.
  213. if (isset($timeout))
  214. {
  215. stream_set_timeout($this->connections[$key], (int) $timeout);
  216. }
  217. return $this->connections[$key];
  218. }
  219. }