TCurlClient.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php
  2. /*
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. *
  20. * @package thrift.transport
  21. */
  22. namespace Thrift\Transport;
  23. use Thrift\Exception\TTransportException;
  24. use Thrift\Factory\TStringFuncFactory;
  25. /**
  26. * HTTP client for Thrift
  27. *
  28. * @package thrift.transport
  29. */
  30. class TCurlClient extends TTransport
  31. {
  32. private static $curlHandle;
  33. /**
  34. * The host to connect to
  35. *
  36. * @var string
  37. */
  38. protected $host_;
  39. /**
  40. * The port to connect on
  41. *
  42. * @var int
  43. */
  44. protected $port_;
  45. /**
  46. * The URI to request
  47. *
  48. * @var string
  49. */
  50. protected $uri_;
  51. /**
  52. * The scheme to use for the request, i.e. http, https
  53. *
  54. * @var string
  55. */
  56. protected $scheme_;
  57. /**
  58. * Buffer for the HTTP request data
  59. *
  60. * @var string
  61. */
  62. protected $request_;
  63. /**
  64. * Buffer for the HTTP response data.
  65. *
  66. * @var binary string
  67. */
  68. protected $response_;
  69. /**
  70. * Read timeout
  71. *
  72. * @var float
  73. */
  74. protected $timeout_;
  75. /**
  76. * http headers
  77. *
  78. * @var array
  79. */
  80. protected $headers_;
  81. /**
  82. * Make a new HTTP client.
  83. *
  84. * @param string $host
  85. * @param int $port
  86. * @param string $uri
  87. */
  88. public function __construct($host, $port=80, $uri='', $scheme = 'http')
  89. {
  90. if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri{0} != '/')) {
  91. $uri = '/'.$uri;
  92. }
  93. $this->scheme_ = $scheme;
  94. $this->host_ = $host;
  95. $this->port_ = $port;
  96. $this->uri_ = $uri;
  97. $this->request_ = '';
  98. $this->response_ = null;
  99. $this->timeout_ = null;
  100. $this->headers_ = array();
  101. }
  102. /**
  103. * Set read timeout
  104. *
  105. * @param float $timeout
  106. */
  107. public function setTimeoutSecs($timeout)
  108. {
  109. $this->timeout_ = $timeout;
  110. }
  111. /**
  112. * Whether this transport is open.
  113. *
  114. * @return boolean true if open
  115. */
  116. public function isOpen()
  117. {
  118. return true;
  119. }
  120. /**
  121. * Open the transport for reading/writing
  122. *
  123. * @throws TTransportException if cannot open
  124. */
  125. public function open()
  126. {
  127. }
  128. /**
  129. * Close the transport.
  130. */
  131. public function close()
  132. {
  133. $this->request_ = '';
  134. $this->response_ = null;
  135. }
  136. /**
  137. * Read some data into the array.
  138. *
  139. * @param int $len How much to read
  140. * @return string The data that has been read
  141. * @throws TTransportException if cannot read any more data
  142. */
  143. public function read($len)
  144. {
  145. if ($len >= strlen($this->response_)) {
  146. return $this->response_;
  147. } else {
  148. $ret = substr($this->response_, 0, $len);
  149. $this->response_ = substr($this->response_, $len);
  150. return $ret;
  151. }
  152. }
  153. /**
  154. * Writes some data into the pending buffer
  155. *
  156. * @param string $buf The data to write
  157. * @throws TTransportException if writing fails
  158. */
  159. public function write($buf)
  160. {
  161. $this->request_ .= $buf;
  162. }
  163. /**
  164. * Opens and sends the actual request over the HTTP connection
  165. *
  166. * @throws TTransportException if a writing error occurs
  167. */
  168. public function flush()
  169. {
  170. if (!self::$curlHandle) {
  171. register_shutdown_function(array('Thrift\\Transport\\TCurlClient', 'closeCurlHandle'));
  172. self::$curlHandle = curl_init();
  173. curl_setopt(self::$curlHandle, CURLOPT_RETURNTRANSFER, true);
  174. curl_setopt(self::$curlHandle, CURLOPT_BINARYTRANSFER, true);
  175. curl_setopt(self::$curlHandle, CURLOPT_USERAGENT, 'PHP/TCurlClient');
  176. curl_setopt(self::$curlHandle, CURLOPT_CUSTOMREQUEST, 'POST');
  177. curl_setopt(self::$curlHandle, CURLOPT_FOLLOWLOCATION, true);
  178. curl_setopt(self::$curlHandle, CURLOPT_MAXREDIRS, 1);
  179. }
  180. // God, PHP really has some esoteric ways of doing simple things.
  181. $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : '');
  182. $fullUrl = $this->scheme_."://".$host.$this->uri_;
  183. $headers = array();
  184. $defaultHeaders = array('Accept' => 'application/x-thrift',
  185. 'Content-Type' => 'application/x-thrift',
  186. 'Content-Length' => TStringFuncFactory::create()->strlen($this->request_));
  187. foreach (array_merge($defaultHeaders, $this->headers_) as $key => $value) {
  188. $headers[] = "$key: $value";
  189. }
  190. curl_setopt(self::$curlHandle, CURLOPT_HTTPHEADER, $headers);
  191. if ($this->timeout_ > 0) {
  192. curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT, $this->timeout_);
  193. }
  194. curl_setopt(self::$curlHandle, CURLOPT_POSTFIELDS, $this->request_);
  195. $this->request_ = '';
  196. curl_setopt(self::$curlHandle, CURLOPT_URL, $fullUrl);
  197. $this->response_ = curl_exec(self::$curlHandle);
  198. // Connect failed?
  199. if (!$this->response_) {
  200. curl_close(self::$curlHandle);
  201. self::$curlHandle = null;
  202. $error = 'TCurlClient: Could not connect to '.$fullUrl;
  203. throw new TTransportException($error, TTransportException::NOT_OPEN);
  204. }
  205. }
  206. public static function closeCurlHandle()
  207. {
  208. try {
  209. if (self::$curlHandle) {
  210. curl_close(self::$curlHandle);
  211. self::$curlHandle = null;
  212. }
  213. } catch (\Exception $x) {
  214. error_log('There was an error closing the curl handle: ' . $x->getMessage());
  215. }
  216. }
  217. public function addHeaders($headers)
  218. {
  219. $this->headers_ = array_merge($this->headers_, $headers);
  220. }
  221. }