PluginProbe
InfiniteWP Client / trunk
InfiniteWP Client vtrunk
1.13.10 1.13.7 trunk 0.1.4 0.1.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.11.0 1.11.1 1.12.1 1.12.3 All 92 releases
iwp-client / lib / Google2 / IO / Stream.php

Stream.php in InfiniteWP Client trunk, at lib/Google2/IO/Stream.php

276 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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
91
92 $url = $request->getUrl();
93
94 if (preg_match('#^https?://([^/]+)/#', $url, $umatches)) { $cname = $umatches[1]; } else { $cname = false; }
95
96
97 // Added
98 if (empty($this->options['disable_verify_peer'])) {
99 $requestSslContext['verify_peer'] = true;
100 if (version_compare(PHP_VERSION, '5.6.0', '>=')) {
101 if (!empty($cname)) $requestSslContext['peer_name'] = $cname;
102 } else {
103 if (!empty($cname)) {
104 $requestSslContext['CN_match'] = $cname;
105 $retry_on_fail = true;
106 }
107 }
108 } else {
109 $requestSslContext['allow_self_signed'] = true;
110 }
111 if (!empty($this->options['cafile'])) $requestSslContext['cafile'] = $this->options['cafile'];
112
113 $options = array(
114 "http" => array_merge(
115 self::$DEFAULT_HTTP_CONTEXT,
116 $requestHttpContext
117 ),
118 "ssl" => array_merge(
119
120 $requestSslContext
121 )
122 );
123
124 $context = stream_context_create($options);
125
126
127
128 if ($request->canGzip()) {
129 $url = self::ZLIB . $url;
130 }
131
132 $this->client->getLogger()->debug(
133 'Stream request',
134 array(
135 'url' => $url,
136 'method' => $request->getRequestMethod(),
137 'headers' => $requestHeaders,
138 'body' => $request->getPostBody()
139 )
140 );
141
142 // We are trapping any thrown errors in this method only and
143 // throwing an exception.
144 $this->trappedErrorNumber = null;
145 $this->trappedErrorString = null;
146
147 // START - error trap.
148 set_error_handler(array($this, 'trapError'));
149 $fh = fopen($url, 'r', false, $context);
150
151 if (!$fh && isset($retry_on_fail) && !empty($cname) && 'www.googleapis.com' == $cname) {
152 // Reset
153 $this->trappedErrorNumber = null;
154 $this->trappedErrorString = null;
155 global $iwp_backup_core;
156 $iwp_backup_core->log("Using Stream, and fopen failed; retrying different CN match to try to overcome");
157 // www.googleapis.com does not match the cert now being presented - *.storage.googleapis.com; presumably, PHP's stream handler isn't handling alternative names properly. Rather than turn off all verification, let's retry with a new name to match.
158 $options['ssl']['CN_match'] = 'www.storage.googleapis.com';
159 $context = stream_context_create($options);
160 $fh = fopen($url, 'r', false, $context);
161 }
162
163 restore_error_handler();
164 // END - error trap.
165
166 if ($this->trappedErrorNumber) {
167 $error = sprintf(
168 "HTTP Error: Unable to connect: '%s'",
169 $this->trappedErrorString
170 );
171
172 $this->client->getLogger()->error('Stream ' . $error);
173 throw new Google_IO_Exception($error, $this->trappedErrorNumber);
174 }
175
176 $response_data = false;
177 $respHttpCode = self::UNKNOWN_CODE;
178 if ($fh) {
179 if (isset($this->options[self::TIMEOUT])) {
180 stream_set_timeout($fh, $this->options[self::TIMEOUT]);
181 }
182
183 $response_data = stream_get_contents($fh);
184 fclose($fh);
185
186 $respHttpCode = $this->getHttpResponseCode($http_response_header);
187 }
188
189 if (false === $response_data) {
190 $error = sprintf(
191 "HTTP Error: Unable to connect: '%s'",
192 $respHttpCode
193 );
194
195 $this->client->getLogger()->error('Stream ' . $error);
196 throw new Google_IO_Exception($error, $respHttpCode);
197 }
198
199 $responseHeaders = $this->getHttpResponseHeaders($http_response_header);
200
201 $this->client->getLogger()->debug(
202 'Stream response',
203 array(
204 'code' => $respHttpCode,
205 'headers' => $responseHeaders,
206 'body' => $response_data,
207 )
208 );
209
210 return array($response_data, $responseHeaders, $respHttpCode);
211 }
212
213 /**
214 * Set options that update the transport implementation's behavior.
215 * @param $options
216 */
217 public function setOptions($options)
218 {
219 $this->options = $options + $this->options;
220 }
221
222 /**
223 * Method to handle errors, used for error handling around
224 * stream connection methods.
225 */
226 public function trapError($errno, $errstr)
227 {
228 $this->trappedErrorNumber = $errno;
229 $this->trappedErrorString = $errstr;
230 }
231
232 /**
233 * Set the maximum request time in seconds.
234 * @param $timeout in seconds
235 */
236 public function setTimeout($timeout)
237 {
238 $this->options[self::TIMEOUT] = $timeout;
239 }
240
241 /**
242 * Get the maximum request time in seconds.
243 * @return timeout in seconds
244 */
245 public function getTimeout()
246 {
247 return $this->options[self::TIMEOUT];
248 }
249
250 /**
251 * Test for the presence of a cURL header processing bug
252 *
253 * {@inheritDoc}
254 *
255 * @return boolean
256 */
257 protected function needsQuirk()
258 {
259 return false;
260 }
261
262 protected function getHttpResponseCode($response_headers)
263 {
264 $header_count = count($response_headers);
265
266 for ($i = 0; $i < $header_count; $i++) {
267 $header = $response_headers[$i];
268 if (strncasecmp("HTTP", $header, strlen("HTTP")) == 0) {
269 $response = explode(' ', $header);
270 return $response[1];
271 }
272 }
273 return self::UNKNOWN_CODE;
274 }
275 }
276