Http.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <?php
  2. namespace Elastica\Transport;
  3. use Elastica\Exception\Connection\HttpException;
  4. use Elastica\Exception\PartialShardFailureException;
  5. use Elastica\Exception\ResponseException;
  6. use Elastica\JSON;
  7. use Elastica\Request;
  8. use Elastica\Response;
  9. use Elastica\Util;
  10. /**
  11. * Elastica Http Transport object.
  12. *
  13. * @author Nicolas Ruflin <spam@ruflin.com>
  14. */
  15. class Http extends AbstractTransport
  16. {
  17. /**
  18. * Http scheme.
  19. *
  20. * @var string Http scheme
  21. */
  22. protected $_scheme = 'http';
  23. /**
  24. * Curl resource to reuse.
  25. *
  26. * @var resource Curl resource to reuse
  27. */
  28. protected static $_curlConnection;
  29. /**
  30. * Makes calls to the elasticsearch server.
  31. *
  32. * All calls that are made to the server are done through this function
  33. *
  34. * @param \Elastica\Request $request
  35. * @param array $params Host, Port, ...
  36. *
  37. * @throws \Elastica\Exception\ConnectionException
  38. * @throws \Elastica\Exception\ResponseException
  39. * @throws \Elastica\Exception\Connection\HttpException
  40. *
  41. * @return \Elastica\Response Response object
  42. */
  43. public function exec(Request $request, array $params)
  44. {
  45. $connection = $this->getConnection();
  46. $conn = $this->_getConnection($connection->isPersistent());
  47. // If url is set, url is taken. Otherwise port, host and path
  48. $url = $connection->hasConfig('url') ? $connection->getConfig('url') : '';
  49. if (!empty($url)) {
  50. $baseUri = $url;
  51. } else {
  52. $baseUri = $this->_scheme.'://'.$connection->getHost().':'.$connection->getPort().'/'.$connection->getPath();
  53. }
  54. $requestPath = $request->getPath();
  55. if (!Util::isDateMathEscaped($requestPath)) {
  56. $requestPath = Util::escapeDateMath($requestPath);
  57. }
  58. $baseUri .= $requestPath;
  59. $query = $request->getQuery();
  60. if (!empty($query)) {
  61. $baseUri .= '?'.http_build_query(
  62. $this->sanityzeQueryStringBool($query)
  63. );
  64. }
  65. curl_setopt($conn, CURLOPT_URL, $baseUri);
  66. curl_setopt($conn, CURLOPT_TIMEOUT, $connection->getTimeout());
  67. curl_setopt($conn, CURLOPT_FORBID_REUSE, 0);
  68. // Tell ES that we support the compressed responses
  69. // An "Accept-Encoding" header containing all supported encoding types is sent
  70. // curl will decode the response automatically if the response is encoded
  71. curl_setopt($conn, CURLOPT_ENCODING, '');
  72. /* @see Connection::setConnectTimeout() */
  73. $connectTimeout = $connection->getConnectTimeout();
  74. if ($connectTimeout > 0) {
  75. curl_setopt($conn, CURLOPT_CONNECTTIMEOUT, $connectTimeout);
  76. }
  77. $proxy = $connection->getProxy();
  78. // See: https://github.com/facebook/hhvm/issues/4875
  79. if (is_null($proxy) && defined('HHVM_VERSION')) {
  80. $proxy = getenv('http_proxy') ?: null;
  81. }
  82. if (!is_null($proxy)) {
  83. curl_setopt($conn, CURLOPT_PROXY, $proxy);
  84. }
  85. $username = $connection->getUsername();
  86. $password = $connection->getPassword();
  87. if (!is_null($username) && !is_null($password)) {
  88. curl_setopt($conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
  89. curl_setopt($conn, CURLOPT_USERPWD, "$username:$password");
  90. }
  91. $this->_setupCurl($conn);
  92. $headersConfig = $connection->hasConfig('headers') ? $connection->getConfig('headers') : [];
  93. $headers = [];
  94. if (!empty($headersConfig)) {
  95. $headers = [];
  96. foreach ($headersConfig as $header => $headerValue) {
  97. array_push($headers, $header.': '.$headerValue);
  98. }
  99. }
  100. // TODO: REFACTOR
  101. $data = $request->getData();
  102. $httpMethod = $request->getMethod();
  103. if (!empty($data) || '0' === $data) {
  104. if ($this->hasParam('postWithRequestBody') && true == $this->getParam('postWithRequestBody')) {
  105. $httpMethod = Request::POST;
  106. }
  107. if (is_array($data)) {
  108. $content = JSON::stringify($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  109. } else {
  110. $content = $data;
  111. // Escaping of / not necessary. Causes problems in base64 encoding of files
  112. $content = str_replace('\/', '/', $content);
  113. }
  114. array_push($headers, sprintf('Content-Type: %s', $request->getContentType()));
  115. if ($connection->hasCompression()) {
  116. // Compress the body of the request ...
  117. curl_setopt($conn, CURLOPT_POSTFIELDS, gzencode($content));
  118. // ... and tell ES that it is compressed
  119. array_push($headers, 'Content-Encoding: gzip');
  120. } else {
  121. curl_setopt($conn, CURLOPT_POSTFIELDS, $content);
  122. }
  123. } else {
  124. curl_setopt($conn, CURLOPT_POSTFIELDS, '');
  125. }
  126. curl_setopt($conn, CURLOPT_HTTPHEADER, $headers);
  127. curl_setopt($conn, CURLOPT_NOBODY, 'HEAD' == $httpMethod);
  128. curl_setopt($conn, CURLOPT_CUSTOMREQUEST, $httpMethod);
  129. $start = microtime(true);
  130. // cURL opt returntransfer leaks memory, therefore OB instead.
  131. ob_start();
  132. curl_exec($conn);
  133. $responseString = ob_get_clean();
  134. $end = microtime(true);
  135. // Checks if error exists
  136. $errorNumber = curl_errno($conn);
  137. $response = new Response($responseString, curl_getinfo($conn, CURLINFO_HTTP_CODE));
  138. $response->setQueryTime($end - $start);
  139. $response->setTransferInfo(curl_getinfo($conn));
  140. if ($connection->hasConfig('bigintConversion')) {
  141. $response->setJsonBigintConversion($connection->getConfig('bigintConversion'));
  142. }
  143. if ($response->hasError()) {
  144. throw new ResponseException($request, $response);
  145. }
  146. if ($response->hasFailedShards()) {
  147. throw new PartialShardFailureException($request, $response);
  148. }
  149. if ($errorNumber > 0) {
  150. throw new HttpException($errorNumber, $request, $response);
  151. }
  152. return $response;
  153. }
  154. /**
  155. * Called to add additional curl params.
  156. *
  157. * @param resource $curlConnection Curl connection
  158. */
  159. protected function _setupCurl($curlConnection)
  160. {
  161. if ($this->getConnection()->hasConfig('curl')) {
  162. foreach ($this->getConnection()->getConfig('curl') as $key => $param) {
  163. curl_setopt($curlConnection, $key, $param);
  164. }
  165. }
  166. }
  167. /**
  168. * Return Curl resource.
  169. *
  170. * @param bool $persistent False if not persistent connection
  171. *
  172. * @return resource Connection resource
  173. */
  174. protected function _getConnection($persistent = true)
  175. {
  176. if (!$persistent || !self::$_curlConnection) {
  177. self::$_curlConnection = curl_init();
  178. }
  179. return self::$_curlConnection;
  180. }
  181. }