HttpRequest.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <?php
  2. namespace Qiniu\Pili;
  3. use \Qiniu\Pili\HttpResponse;
  4. class HttpRequest
  5. {
  6. const DELETE = "DELETE";
  7. const GET = "GET";
  8. const POST = "POST";
  9. private static $verifyPeer = false;
  10. private static $socketTimeout = null;
  11. private static $defaultHeaders = array();
  12. /**
  13. * Verify SSL peer
  14. * @param bool $enabled enable SSL verification, by default is false
  15. */
  16. public static function verifyPeer($enabled)
  17. {
  18. self::$verifyPeer = $enabled;
  19. }
  20. /**
  21. * Set a timeout
  22. * @param integer $seconds timeout value in seconds
  23. */
  24. public static function timeout($seconds)
  25. {
  26. self::$socketTimeout = $seconds;
  27. }
  28. /**
  29. * Set a new default header to send on every request
  30. * @param string $name header name
  31. * @param string $value header value
  32. */
  33. public static function defaultHeader($name, $value)
  34. {
  35. self::$defaultHeaders[$name] = $value;
  36. }
  37. /**
  38. * Clear all the default headers
  39. */
  40. public static function clearDefaultHeaders()
  41. {
  42. self::$defaultHeaders = array();
  43. }
  44. /**
  45. * This function is useful for serializing multidimensional arrays, and avoid getting
  46. * the "Array to string conversion" notice
  47. */
  48. public static function http_build_query_for_curl($arrays, &$new = array(), $prefix = null)
  49. {
  50. if (is_object($arrays)) {
  51. $arrays = get_object_vars($arrays);
  52. }
  53. foreach ($arrays as $key => $value) {
  54. $k = isset($prefix) ? $prefix . '[' . $key . ']' : $key;
  55. if (!$value instanceof \CURLFile and (is_array($value) or is_object($value))) {
  56. self::http_build_query_for_curl($value, $new, $k);
  57. } else {
  58. $new[$k] = $value;
  59. }
  60. }
  61. }
  62. private static function getArrayFromQuerystring($querystring)
  63. {
  64. $pairs = explode("&", $querystring);
  65. $vars = array();
  66. foreach ($pairs as $pair) {
  67. $nv = explode("=", $pair, 2);
  68. $name = $nv[0];
  69. $value = $nv[1];
  70. $vars[$name] = $value;
  71. }
  72. return $vars;
  73. }
  74. /**
  75. * Ensure that a URL is encoded and safe to use with cURL
  76. * @param string $url URL to encode
  77. * @return string
  78. */
  79. private static function encodeUrl($url)
  80. {
  81. $url_parsed = parse_url($url);
  82. $scheme = $url_parsed['scheme'] . '://';
  83. $host = $url_parsed['host'];
  84. $port = (isset($url_parsed['port']) ? $url_parsed['port'] : null);
  85. $path = (isset($url_parsed['path']) ? $url_parsed['path'] : null);
  86. $query = (isset($url_parsed['query']) ? $url_parsed['query'] : null);
  87. if ($query != null) {
  88. $query = '?' . http_build_query(self::getArrayFromQuerystring($url_parsed['query']));
  89. }
  90. if ($port && $port[0] != ":") {
  91. $port = ":" . $port;
  92. }
  93. $result = $scheme . $host . $port . $path . $query;
  94. return $result;
  95. }
  96. private static function getHeader($key, $val)
  97. {
  98. $key = trim($key);
  99. return $key . ": " . $val;
  100. }
  101. /**
  102. * Send a cURL request
  103. * @param string $httpMethod HTTP method to use
  104. * @param string $url URL to send the request to
  105. * @param mixed $body request body
  106. * @param array $headers additional headers to send
  107. * @throws Exception if a cURL error occurs
  108. * @return HttpResponse
  109. */
  110. public static function send($httpMethod, $url, $body = null, $headers = array())
  111. {
  112. if ($headers == null) {
  113. $headers = array();
  114. }
  115. $annexHeaders = array();
  116. $finalHeaders = array_merge($headers, self::$defaultHeaders);
  117. foreach ($finalHeaders as $key => $val) {
  118. $annexHeaders[] = self::getHeader($key, $val);
  119. }
  120. $lowerCaseFinalHeaders = array_change_key_case($finalHeaders);
  121. $ch = curl_init();
  122. if ($httpMethod != self::GET) {
  123. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpMethod);
  124. if (is_array($body) || $body instanceof Traversable) {
  125. self::http_build_query_for_curl($body, $postBody);
  126. curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
  127. } else {
  128. curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
  129. }
  130. } elseif (is_array($body)) {
  131. if (strpos($url, '?') !== false) {
  132. $url .= "&";
  133. } else {
  134. $url .= "?";
  135. }
  136. self::http_build_query_for_curl($body, $postBody);
  137. $url .= urldecode(http_build_query($postBody));
  138. }
  139. curl_setopt($ch, CURLOPT_URL, self::encodeUrl($url));
  140. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  141. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  142. curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
  143. curl_setopt($ch, CURLOPT_HTTPHEADER, $annexHeaders);
  144. curl_setopt($ch, CURLOPT_HEADER, true);
  145. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, self::$verifyPeer);
  146. curl_setopt($ch, CURLOPT_ENCODING, ""); // If an empty string, "", is set, a header containing all supported encoding types is sent.
  147. if (self::$socketTimeout != null) {
  148. curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);
  149. }
  150. $response = curl_exec($ch);
  151. $error = curl_error($ch);
  152. if ($error) {
  153. throw new \Exception($error);
  154. }
  155. // Split the full response in its headers and body
  156. $curl_info = curl_getinfo($ch);
  157. $header_size = $curl_info["header_size"];
  158. $header = substr($response, 0, $header_size);
  159. $body = substr($response, $header_size);
  160. $httpCode = $curl_info["http_code"];
  161. if ($httpCode >= 400) {
  162. throw new \Exception($body);
  163. }
  164. return new HttpResponse($httpCode, $body, $header);
  165. }
  166. }