See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 402] [Versions 39 and 403]
1 <?php 2 /* 3 * Copyright 2013 Google Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /** 19 * Http Streams based implementation of Google_IO. 20 * 21 * @author Stuart Langley <slangley@google.com> 22 */ 23 24 if (!class_exists('Google_Client')) { 25 require_once dirname(__FILE__) . '/../autoload.php'; 26 } 27 28 class Google_IO_Stream extends Google_IO_Abstract 29 { 30 const TIMEOUT = "timeout"; 31 const ZLIB = "compress.zlib://"; 32 private $options = array(); 33 private $trappedErrorNumber; 34 private $trappedErrorString; 35 36 private static $DEFAULT_HTTP_CONTEXT = array( 37 "follow_location" => 0, 38 "ignore_errors" => 1, 39 ); 40 41 private static $DEFAULT_SSL_CONTEXT = array( 42 "verify_peer" => true, 43 ); 44 45 public function __construct(Google_Client $client) 46 { 47 if (!ini_get('allow_url_fopen')) { 48 $error = 'The stream IO handler requires the allow_url_fopen runtime ' . 49 'configuration to be enabled'; 50 $client->getLogger()->critical($error); 51 throw new Google_IO_Exception($error); 52 } 53 54 parent::__construct($client); 55 } 56 57 /** 58 * Execute an HTTP Request 59 * 60 * @param Google_Http_Request $request the http request to be executed 61 * @return array containing response headers, body, and http code 62 * @throws Google_IO_Exception on curl or IO error 63 */ 64 public function executeRequest(Google_Http_Request $request) 65 { 66 $default_options = stream_context_get_options(stream_context_get_default()); 67 68 $requestHttpContext = array_key_exists('http', $default_options) ? 69 $default_options['http'] : array(); 70 71 if ($request->getPostBody()) { 72 $requestHttpContext["content"] = $request->getPostBody(); 73 } 74 75 $requestHeaders = $request->getRequestHeaders(); 76 if ($requestHeaders && is_array($requestHeaders)) { 77 $headers = ""; 78 foreach ($requestHeaders as $k => $v) { 79 $headers .= "$k: $v\r\n"; 80 } 81 $requestHttpContext["header"] = $headers; 82 } 83 84 $requestHttpContext["method"] = $request->getRequestMethod(); 85 $requestHttpContext["user_agent"] = $request->getUserAgent(); 86 87 $requestSslContext = array_key_exists('ssl', $default_options) ? 88 $default_options['ssl'] : array(); 89 90 if (!$this->client->isAppEngine() && !array_key_exists("cafile", $requestSslContext)) { 91 $requestSslContext["cafile"] = dirname(__FILE__) . '/cacerts.pem'; 92 } 93 94 $options = array( 95 "http" => array_merge( 96 self::$DEFAULT_HTTP_CONTEXT, 97 $requestHttpContext 98 ), 99 "ssl" => array_merge( 100 self::$DEFAULT_SSL_CONTEXT, 101 $requestSslContext 102 ) 103 ); 104 105 $context = stream_context_create($options); 106 107 $url = $request->getUrl(); 108 109 if ($request->canGzip()) { 110 $url = self::ZLIB . $url; 111 } 112 113 $this->client->getLogger()->debug( 114 'Stream request', 115 array( 116 'url' => $url, 117 'method' => $request->getRequestMethod(), 118 'headers' => $requestHeaders, 119 'body' => $request->getPostBody() 120 ) 121 ); 122 123 // We are trapping any thrown errors in this method only and 124 // throwing an exception. 125 $this->trappedErrorNumber = null; 126 $this->trappedErrorString = null; 127 128 // START - error trap. 129 set_error_handler(array($this, 'trapError')); 130 $fh = fopen($url, 'r', false, $context); 131 restore_error_handler(); 132 // END - error trap. 133 134 if ($this->trappedErrorNumber) { 135 $error = sprintf( 136 "HTTP Error: Unable to connect: '%s'", 137 $this->trappedErrorString 138 ); 139 140 $this->client->getLogger()->error('Stream ' . $error); 141 throw new Google_IO_Exception($error, $this->trappedErrorNumber); 142 } 143 144 $response_data = false; 145 $respHttpCode = self::UNKNOWN_CODE; 146 if ($fh) { 147 if (isset($this->options[self::TIMEOUT])) { 148 stream_set_timeout($fh, $this->options[self::TIMEOUT]); 149 } 150 151 $response_data = stream_get_contents($fh); 152 fclose($fh); 153 154 $respHttpCode = $this->getHttpResponseCode($http_response_header); 155 } 156 157 if (false === $response_data) { 158 $error = sprintf( 159 "HTTP Error: Unable to connect: '%s'", 160 $respHttpCode 161 ); 162 163 $this->client->getLogger()->error('Stream ' . $error); 164 throw new Google_IO_Exception($error, $respHttpCode); 165 } 166 167 $responseHeaders = $this->getHttpResponseHeaders($http_response_header); 168 169 $this->client->getLogger()->debug( 170 'Stream response', 171 array( 172 'code' => $respHttpCode, 173 'headers' => $responseHeaders, 174 'body' => $response_data, 175 ) 176 ); 177 178 return array($response_data, $responseHeaders, $respHttpCode); 179 } 180 181 /** 182 * Set options that update the transport implementation's behavior. 183 * @param $options 184 */ 185 public function setOptions($options) 186 { 187 $this->options = $options + $this->options; 188 } 189 190 /** 191 * Method to handle errors, used for error handling around 192 * stream connection methods. 193 */ 194 public function trapError($errno, $errstr) 195 { 196 $this->trappedErrorNumber = $errno; 197 $this->trappedErrorString = $errstr; 198 } 199 200 /** 201 * Set the maximum request time in seconds. 202 * @param $timeout in seconds 203 */ 204 public function setTimeout($timeout) 205 { 206 $this->options[self::TIMEOUT] = $timeout; 207 } 208 209 /** 210 * Get the maximum request time in seconds. 211 * @return timeout in seconds 212 */ 213 public function getTimeout() 214 { 215 return $this->options[self::TIMEOUT]; 216 } 217 218 /** 219 * Test for the presence of a cURL header processing bug 220 * 221 * {@inheritDoc} 222 * 223 * @return boolean 224 */ 225 protected function needsQuirk() 226 { 227 return false; 228 } 229 230 protected function getHttpResponseCode($response_headers) 231 { 232 $header_count = count($response_headers); 233 234 for ($i = 0; $i < $header_count; $i++) { 235 $header = $response_headers[$i]; 236 if (strncasecmp("HTTP", $header, strlen("HTTP")) == 0) { 237 $response = explode(' ', $header); 238 return $response[1]; 239 } 240 } 241 return self::UNKNOWN_CODE; 242 } 243 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body