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 / Google / IO / Abstract.php

Abstract.php in InfiniteWP Client trunk, at lib/Google/IO/Abstract.php

313 lines 10.2 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 * Abstract IO base class
20 */
21
22 require_once $GLOBALS['iwp_mmb_plugin_dir'].'/lib/Google/Client.php';
23 require_once $GLOBALS['iwp_mmb_plugin_dir'].'/lib/Google/IO/Exception.php';
24 require_once $GLOBALS['iwp_mmb_plugin_dir'].'/lib/Google/Http/CacheParser.php';
25 require_once $GLOBALS['iwp_mmb_plugin_dir'].'/lib/Google/Http/Request.php';
26
27 abstract class IWP_google_IO_Abstract
28 {
29 const UNKNOWN_CODE = 0;
30 const FORM_URLENCODED = 'application/x-www-form-urlencoded';
31 const CONNECTION_ESTABLISHED = "HTTP/1.0 200 Connection established\r\n\r\n";
32 private static $ENTITY_HTTP_METHODS = array("POST" => null, "PUT" => null);
33
34 /** @var IWP_google_Client */
35 protected $client;
36
37 public function __construct(IWP_google_Client $client)
38 {
39 $this->client = $client;
40 $timeout = $client->getClassConfig('IWP_google_IO_Abstract', 'request_timeout_seconds');
41 if ($timeout > 0) {
42 $this->setTimeout($timeout);
43 }
44 }
45
46 /**
47 * Executes a IWP_google_Http_Request and returns the resulting populated IWP_google_Http_Request
48 * @param IWP_google_Http_Request $request
49 * @return IWP_google_Http_Request $request
50 */
51 abstract public function executeRequest(IWP_google_Http_Request $request);
52
53 /**
54 * Set options that update the transport implementation's behavior.
55 * @param $options
56 */
57 abstract public function setOptions($options);
58
59 /**
60 * Set the maximum request time in seconds.
61 * @param $timeout in seconds
62 */
63 abstract public function setTimeout($timeout);
64
65 /**
66 * Get the maximum request time in seconds.
67 * @return timeout in seconds
68 */
69 abstract public function getTimeout();
70
71 /**
72 * Determine whether "Connection Established" quirk is needed
73 * @return boolean
74 */
75 abstract protected function needsQuirk();
76
77 /**
78 * @visible for testing.
79 * Cache the response to an HTTP request if it is cacheable.
80 * @param IWP_google_Http_Request $request
81 * @return bool Returns true if the insertion was successful.
82 * Otherwise, return false.
83 */
84 public function setCachedRequest(IWP_google_Http_Request $request)
85 {
86 // Determine if the request is cacheable.
87 if (IWP_google_Http_CacheParser::isResponseCacheable($request)) {
88 $this->client->getCache()->set($request->getCacheKey(), $request);
89 return true;
90 }
91
92 return false;
93 }
94
95 /**
96 * Execute an HTTP Request
97 *
98 * @param IWP_google_HttpRequest $request the http request to be executed
99 * @return IWP_google_HttpRequest http request with the response http code,
100 * response headers and response body filled in
101 * @throws IWP_google_IO_Exception on curl or IO error
102 */
103 public function makeRequest(IWP_google_Http_Request $request)
104 {
105 // First, check to see if we have a valid cached version.
106 $cached = $this->getCachedRequest($request);
107 if ($cached !== false && $cached instanceof IWP_google_Http_Request) {
108 if (!$this->checkMustRevalidateCachedRequest($cached, $request)) {
109 return $cached;
110 }
111 }
112
113 if (array_key_exists($request->getRequestMethod(), self::$ENTITY_HTTP_METHODS)) {
114 $request = $this->processEntityRequest($request);
115 }
116
117 list($responseData, $responseHeaders, $respHttpCode) = $this->executeRequest($request);
118
119 if ($respHttpCode == 304 && $cached) {
120 // If the server responded NOT_MODIFIED, return the cached request.
121 $this->updateCachedRequest($cached, $responseHeaders);
122 return $cached;
123 }
124
125 if (!isset($responseHeaders['Date']) && !isset($responseHeaders['date'])) {
126 $responseHeaders['Date'] = date("r");
127 }
128
129 $request->setResponseHttpCode($respHttpCode);
130 $request->setResponseHeaders($responseHeaders);
131 $request->setResponseBody($responseData);
132 // Store the request in cache (the function checks to see if the request
133 // can actually be cached)
134 $this->setCachedRequest($request);
135 return $request;
136 }
137
138 /**
139 * @visible for testing.
140 * @param IWP_google_Http_Request $request
141 * @return IWP_google_Http_Request|bool Returns the cached object or
142 * false if the operation was unsuccessful.
143 */
144 public function getCachedRequest(IWP_google_Http_Request $request)
145 {
146 if (false === IWP_google_Http_CacheParser::isRequestCacheable($request)) {
147 return false;
148 }
149
150 return $this->client->getCache()->get($request->getCacheKey());
151 }
152
153 /**
154 * @visible for testing
155 * Process an http request that contains an enclosed entity.
156 * @param IWP_google_Http_Request $request
157 * @return IWP_google_Http_Request Processed request with the enclosed entity.
158 */
159 public function processEntityRequest(IWP_google_Http_Request $request)
160 {
161 $postBody = $request->getPostBody();
162 $contentType = $request->getRequestHeader("content-type");
163
164 // Set the default content-type as application/x-www-form-urlencoded.
165 if (false == $contentType) {
166 $contentType = self::FORM_URLENCODED;
167 $request->setRequestHeaders(array('content-type' => $contentType));
168 }
169
170 // Force the payload to match the content-type asserted in the header.
171 if ($contentType == self::FORM_URLENCODED && is_array($postBody)) {
172 $postBody = http_build_query($postBody, '', '&');
173 $request->setPostBody($postBody);
174 }
175
176 // Make sure the content-length header is set.
177 if (!$postBody || is_string($postBody)) {
178 $postsLength = strlen($postBody);
179 $request->setRequestHeaders(array('content-length' => $postsLength));
180 }
181
182 return $request;
183 }
184
185 /**
186 * Check if an already cached request must be revalidated, and if so update
187 * the request with the correct ETag headers.
188 * @param IWP_google_Http_Request $cached A previously cached response.
189 * @param IWP_google_Http_Request $request The outbound request.
190 * return bool If the cached object needs to be revalidated, false if it is
191 * still current and can be re-used.
192 */
193 protected function checkMustRevalidateCachedRequest($cached, $request)
194 {
195 if (IWP_google_Http_CacheParser::mustRevalidate($cached)) {
196 $addHeaders = array();
197 if ($cached->getResponseHeader('etag')) {
198 // [13.3.4] If an entity tag has been provided by the origin server,
199 // we must use that entity tag in any cache-conditional request.
200 $addHeaders['If-None-Match'] = $cached->getResponseHeader('etag');
201 } elseif ($cached->getResponseHeader('date')) {
202 $addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date');
203 }
204
205 $request->setRequestHeaders($addHeaders);
206 return true;
207 } else {
208 return false;
209 }
210 }
211
212 /**
213 * Update a cached request, using the headers from the last response.
214 * @param IWP_google_HttpRequest $cached A previously cached response.
215 * @param mixed Associative array of response headers from the last request.
216 */
217 protected function updateCachedRequest($cached, $responseHeaders)
218 {
219 if (isset($responseHeaders['connection'])) {
220 $hopByHop = array_merge(
221 self::$HOP_BY_HOP,
222 explode(
223 ',',
224 $responseHeaders['connection']
225 )
226 );
227
228 $endToEnd = array();
229 foreach ($hopByHop as $key) {
230 if (isset($responseHeaders[$key])) {
231 $endToEnd[$key] = $responseHeaders[$key];
232 }
233 }
234 $cached->setResponseHeaders($endToEnd);
235 }
236 }
237
238 /**
239 * Used by the IO lib and also the batch processing.
240 *
241 * @param $respData
242 * @param $headerSize
243 * @return array
244 */
245 public function parseHttpResponse($respData, $headerSize)
246 {
247 // only strip this header if the sub-class needs this quirk
248 if ($this->needsQuirk() && stripos($respData, self::CONNECTION_ESTABLISHED) !== false) {
249 $respData = str_ireplace(self::CONNECTION_ESTABLISHED, '', $respData);
250 $headerSize -= strlen(self::CONNECTION_ESTABLISHED);
251 }
252
253 if ($headerSize) {
254 $responseBody = substr($respData, $headerSize);
255 $responseHeaders = substr($respData, 0, $headerSize);
256 } else {
257 list($responseHeaders, $responseBody) = explode("\r\n\r\n", $respData, 2);
258 }
259
260 $responseHeaders = $this->getHttpResponseHeaders($responseHeaders);
261 return array($responseHeaders, $responseBody);
262 }
263
264 /**
265 * Parse out headers from raw headers
266 * @param rawHeaders array or string
267 * @return array
268 */
269 public function getHttpResponseHeaders($rawHeaders)
270 {
271 if (is_array($rawHeaders)) {
272 return $this->parseArrayHeaders($rawHeaders);
273 } else {
274 return $this->parseStringHeaders($rawHeaders);
275 }
276 }
277
278 private function parseStringHeaders($rawHeaders)
279 {
280 $headers = array();
281 $responseHeaderLines = explode("\r\n", $rawHeaders);
282 foreach ($responseHeaderLines as $headerLine) {
283 if ($headerLine && strpos($headerLine, ':') !== false) {
284 list($header, $value) = explode(': ', $headerLine, 2);
285 $header = strtolower($header);
286 if (isset($responseHeaders[$header])) {
287 $headers[$header] .= "\n" . $value;
288 } else {
289 $headers[$header] = $value;
290 }
291 }
292 }
293 return $headers;
294 }
295
296 private function parseArrayHeaders($rawHeaders)
297 {
298 $header_count = count($rawHeaders);
299 $headers = array();
300
301 for ($i = 0; $i < $header_count; $i++) {
302 $header = $rawHeaders[$i];
303 // Times will have colons in - so we just want the first match.
304 $header_parts = explode(': ', $header, 2);
305 if (count($header_parts) == 2) {
306 $headers[$header_parts[0]] = $header_parts[1];
307 }
308 }
309
310 return $headers;
311 }
312 }
313