Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
#[AllowDynamicProperties]
29
class Google_IO_Stream extends Google_IO_Abstract
30
{
31
  const TIMEOUT = "timeout";
32
  const ZLIB = "compress.zlib://";
33
  private $options = array();
34
  private $trappedErrorNumber;
35
  private $trappedErrorString;
36
 
37
  private static $DEFAULT_HTTP_CONTEXT = array(
38
    "follow_location" => 0,
39
    "ignore_errors" => 1,
40
  );
41
 
42
  private static $DEFAULT_SSL_CONTEXT = array(
43
    "verify_peer" => true,
44
  );
45
 
46
  public function __construct(Google_Client $client)
47
  {
48
    if (!ini_get('allow_url_fopen')) {
49
      $error = 'The stream IO handler requires the allow_url_fopen runtime ' .
50
               'configuration to be enabled';
51
      $client->getLogger()->critical($error);
52
      throw new Google_IO_Exception($error);
53
    }
54
 
55
    parent::__construct($client);
56
  }
57
 
58
  /**
59
   * Execute an HTTP Request
60
   *
61
   * @param Google_Http_Request $request the http request to be executed
62
   * @return array containing response headers, body, and http code
63
   * @throws Google_IO_Exception on curl or IO error
64
   */
65
  public function executeRequest(Google_Http_Request $request)
66
  {
67
    $default_options = stream_context_get_options(stream_context_get_default());
68
 
69
    $requestHttpContext = array_key_exists('http', $default_options) ?
70
        $default_options['http'] : array();
71
 
72
    if ($request->getPostBody()) {
73
      $requestHttpContext["content"] = $request->getPostBody();
74
    }
75
 
76
    $requestHeaders = $request->getRequestHeaders();
77
    if ($requestHeaders && is_array($requestHeaders)) {
78
      $headers = "";
79
      foreach ($requestHeaders as $k => $v) {
80
        $headers .= "$k: $v\r\n";
81
      }
82
      $requestHttpContext["header"] = $headers;
83
    }
84
 
85
    $requestHttpContext["method"] = $request->getRequestMethod();
86
    $requestHttpContext["user_agent"] = $request->getUserAgent();
87
 
88
    $requestSslContext = array_key_exists('ssl', $default_options) ?
89
        $default_options['ssl'] : array();
90
 
91
    if (!$this->client->isAppEngine() && !array_key_exists("cafile", $requestSslContext)) {
92
      $requestSslContext["cafile"] = dirname(__FILE__) . '/cacerts.pem';
93
    }
94
 
95
    $options = array(
96
        "http" => array_merge(
97
            self::$DEFAULT_HTTP_CONTEXT,
98
            $requestHttpContext
99
        ),
100
        "ssl" => array_merge(
101
            self::$DEFAULT_SSL_CONTEXT,
102
            $requestSslContext
103
        )
104
    );
105
 
106
    $context = stream_context_create($options);
107
 
108
    $url = $request->getUrl();
109
 
110
    if ($request->canGzip()) {
111
      $url = self::ZLIB . $url;
112
    }
113
 
114
    $this->client->getLogger()->debug(
115
        'Stream request',
116
        array(
117
            'url' => $url,
118
            'method' => $request->getRequestMethod(),
119
            'headers' => $requestHeaders,
120
            'body' => $request->getPostBody()
121
        )
122
    );
123
 
124
    // We are trapping any thrown errors in this method only and
125
    // throwing an exception.
126
    $this->trappedErrorNumber = null;
127
    $this->trappedErrorString = null;
128
 
129
    // START - error trap.
130
    set_error_handler(array($this, 'trapError'));
131
    $fh = fopen($url, 'r', false, $context);
132
    restore_error_handler();
133
    // END - error trap.
134
 
135
    if ($this->trappedErrorNumber) {
136
      $error = sprintf(
137
          "HTTP Error: Unable to connect: '%s'",
138
          $this->trappedErrorString
139
      );
140
 
141
      $this->client->getLogger()->error('Stream ' . $error);
142
      throw new Google_IO_Exception($error, $this->trappedErrorNumber);
143
    }
144
 
145
    $response_data = false;
146
    $respHttpCode = self::UNKNOWN_CODE;
147
    if ($fh) {
148
      if (isset($this->options[self::TIMEOUT])) {
149
        stream_set_timeout($fh, $this->options[self::TIMEOUT]);
150
      }
151
 
152
      $response_data = stream_get_contents($fh);
153
      fclose($fh);
154
 
155
      $respHttpCode = $this->getHttpResponseCode($http_response_header);
156
    }
157
 
158
    if (false === $response_data) {
159
      $error = sprintf(
160
          "HTTP Error: Unable to connect: '%s'",
161
          $respHttpCode
162
      );
163
 
164
      $this->client->getLogger()->error('Stream ' . $error);
165
      throw new Google_IO_Exception($error, $respHttpCode);
166
    }
167
 
168
    $responseHeaders = $this->getHttpResponseHeaders($http_response_header);
169
 
170
    $this->client->getLogger()->debug(
171
        'Stream response',
172
        array(
173
            'code' => $respHttpCode,
174
            'headers' => $responseHeaders,
175
            'body' => $response_data,
176
        )
177
    );
178
 
179
    return array($response_data, $responseHeaders, $respHttpCode);
180
  }
181
 
182
  /**
183
   * Set options that update the transport implementation's behavior.
184
   * @param $options
185
   */
186
  public function setOptions($options)
187
  {
188
    $this->options = $options + $this->options;
189
  }
190
 
191
  /**
192
   * Method to handle errors, used for error handling around
193
   * stream connection methods.
194
   */
195
  public function trapError($errno, $errstr)
196
  {
197
    $this->trappedErrorNumber = $errno;
198
    $this->trappedErrorString = $errstr;
199
  }
200
 
201
  /**
202
   * Set the maximum request time in seconds.
203
   * @param $timeout in seconds
204
   */
205
  public function setTimeout($timeout)
206
  {
207
    $this->options[self::TIMEOUT] = $timeout;
208
  }
209
 
210
  /**
211
   * Get the maximum request time in seconds.
212
   * @return timeout in seconds
213
   */
214
  public function getTimeout()
215
  {
216
    return $this->options[self::TIMEOUT];
217
  }
218
 
219
  /**
220
   * Test for the presence of a cURL header processing bug
221
   *
222
   * {@inheritDoc}
223
   *
224
   * @return boolean
225
   */
226
  protected function needsQuirk()
227
  {
228
    return false;
229
  }
230
 
231
  protected function getHttpResponseCode($response_headers)
232
  {
233
    $header_count = count($response_headers);
234
 
235
    for ($i = 0; $i < $header_count; $i++) {
236
      $header = $response_headers[$i];
237
      if (strncasecmp("HTTP", $header, strlen("HTTP")) == 0) {
238
        $response = explode(' ', $header);
239
        return $response[1];
240
      }
241
    }
242
    return self::UNKNOWN_CODE;
243
  }
244
}