1<?php
2/**
3 * Copyright 2010 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
18require_once 'io/Google_HttpRequest.php';
19require_once 'io/Google_HttpStreamIO.php';
20require_once 'io/Google_CurlIO.php';
21require_once 'io/Google_REST.php';
22
23/**
24 * Abstract IO class
25 *
26 * @author Chris Chabot <chabotc@google.com>
27 */
28abstract class Google_IO {
29  const CONNECTION_ESTABLISHED = "HTTP/1.0 200 Connection established\r\n\r\n";
30  const FORM_URLENCODED = 'application/x-www-form-urlencoded';
31  /**
32   * An utility function that first calls $this->auth->sign($request) and then executes makeRequest()
33   * on that signed request. Used for when a request should be authenticated
34   * @param Google_HttpRequest $request
35   * @return Google_HttpRequest $request
36   */
37  abstract function authenticatedRequest(Google_HttpRequest $request);
38
39  /**
40   * Executes a apIHttpRequest and returns the resulting populated httpRequest
41   * @param Google_HttpRequest $request
42   * @return Google_HttpRequest $request
43   */
44  abstract function makeRequest(Google_HttpRequest $request);
45
46  /**
47   * Set options that update the transport implementation's behavior.
48   * @param $options
49   */
50  abstract function setOptions($options);
51
52  /**
53   * @visible for testing.
54   * Cache the response to an HTTP request if it is cacheable.
55   * @param Google_HttpRequest $request
56   * @return bool Returns true if the insertion was successful.
57   * Otherwise, return false.
58   */
59  protected function setCachedRequest(Google_HttpRequest $request) {
60    // Determine if the request is cacheable.
61    if (Google_CacheParser::isResponseCacheable($request)) {
62      Google_Client::$cache->set($request->getCacheKey(), $request);
63      return true;
64    }
65
66    return false;
67  }
68
69  /**
70   * @visible for testing.
71   * @param Google_HttpRequest $request
72   * @return Google_HttpRequest|bool Returns the cached object or
73   * false if the operation was unsuccessful.
74   */
75  protected function getCachedRequest(Google_HttpRequest $request) {
76    if (false == Google_CacheParser::isRequestCacheable($request)) {
77      false;
78    }
79
80    return Google_Client::$cache->get($request->getCacheKey());
81  }
82
83  /**
84   * @visible for testing
85   * Process an http request that contains an enclosed entity.
86   * @param Google_HttpRequest $request
87   * @return Google_HttpRequest Processed request with the enclosed entity.
88   */
89  protected function processEntityRequest(Google_HttpRequest $request) {
90    $postBody = $request->getPostBody();
91    $contentType = $request->getRequestHeader("content-type");
92
93    // Set the default content-type as application/x-www-form-urlencoded.
94    if (false == $contentType) {
95      $contentType = self::FORM_URLENCODED;
96      $request->setRequestHeaders(array('content-type' => $contentType));
97    }
98
99    // Force the payload to match the content-type asserted in the header.
100    if ($contentType == self::FORM_URLENCODED && is_array($postBody)) {
101      $postBody = http_build_query($postBody, '', '&');
102      $request->setPostBody($postBody);
103    }
104
105    // Make sure the content-length header is set.
106    if (!$postBody || is_string($postBody)) {
107      $postsLength = strlen($postBody);
108      $request->setRequestHeaders(array('content-length' => $postsLength));
109    }
110
111    return $request;
112  }
113
114  /**
115   * Check if an already cached request must be revalidated, and if so update
116   * the request with the correct ETag headers.
117   * @param Google_HttpRequest $cached A previously cached response.
118   * @param Google_HttpRequest $request The outbound request.
119   * return bool If the cached object needs to be revalidated, false if it is
120   * still current and can be re-used.
121   */
122  protected function checkMustRevaliadateCachedRequest($cached, $request) {
123    if (Google_CacheParser::mustRevalidate($cached)) {
124      $addHeaders = array();
125      if ($cached->getResponseHeader('etag')) {
126        // [13.3.4] If an entity tag has been provided by the origin server,
127        // we must use that entity tag in any cache-conditional request.
128        $addHeaders['If-None-Match'] = $cached->getResponseHeader('etag');
129      } elseif ($cached->getResponseHeader('date')) {
130        $addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date');
131      }
132
133      $request->setRequestHeaders($addHeaders);
134      return true;
135    } else {
136      return false;
137    }
138  }
139
140  /**
141   * Update a cached request, using the headers from the last response.
142   * @param Google_HttpRequest $cached A previously cached response.
143   * @param mixed Associative array of response headers from the last request.
144   */
145  protected function updateCachedRequest($cached, $responseHeaders) {
146    if (isset($responseHeaders['connection'])) {
147      $hopByHop = array_merge(
148        self::$HOP_BY_HOP,
149        explode(',', $responseHeaders['connection'])
150      );
151
152      $endToEnd = array();
153      foreach($hopByHop as $key) {
154        if (isset($responseHeaders[$key])) {
155          $endToEnd[$key] = $responseHeaders[$key];
156        }
157      }
158      $cached->setResponseHeaders($endToEnd);
159    }
160  }
161}
162