MediaWiki  master
GuzzleHttpRequest.php
Go to the documentation of this file.
1 <?php
21 use GuzzleHttp\Client;
22 use GuzzleHttp\Psr7\Request;
23 
40  const SUPPORTS_FILE_POSTS = true;
41 
42  protected $handler = null;
43  protected $sink = null;
45  protected $guzzleOptions = [ 'http_errors' => false ];
46 
54  public function __construct(
55  $url, array $options = [], $caller = __METHOD__, Profiler $profiler = null
56  ) {
57  parent::__construct( $url, $options, $caller, $profiler );
58 
59  if ( isset( $options['handler'] ) ) {
60  $this->handler = $options['handler'];
61  }
62  if ( isset( $options['sink'] ) ) {
63  $this->sink = $options['sink'];
64  }
65  }
66 
87  public function setCallback( $callback ) {
88  $this->sink = null;
89  $this->doSetCallback( $callback );
90  }
91 
102  protected function doSetCallback( $callback ) {
103  if ( !$this->sink ) {
104  parent::doSetCallback( $callback );
105  $this->sink = new MWCallbackStream( $this->callback );
106  }
107  }
108 
114  public function execute() {
115  $this->prepare();
116 
117  if ( !$this->status->isOK() ) {
118  return Status::wrap( $this->status ); // TODO B/C; move this to callers
119  }
120 
121  if ( $this->proxy ) {
122  $this->guzzleOptions['proxy'] = $this->proxy;
123  }
124 
125  $this->guzzleOptions['timeout'] = $this->timeout;
126  $this->guzzleOptions['connect_timeout'] = $this->connectTimeout;
127  $this->guzzleOptions['version'] = '1.1';
128 
129  if ( !$this->followRedirects ) {
130  $this->guzzleOptions['allow_redirects'] = false;
131  } else {
132  $this->guzzleOptions['allow_redirects'] = [
133  'max' => $this->maxRedirects
134  ];
135  }
136 
137  if ( $this->method == 'POST' ) {
139  if ( is_array( $postData ) ) {
140  $this->guzzleOptions['form_params'] = $postData;
141  } else {
142  $this->guzzleOptions['body'] = $postData;
143  // mimic CURLOPT_POST option
144  if ( !isset( $this->reqHeaders['Content-Type'] ) ) {
145  $this->reqHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
146  }
147  }
148 
149  // Suppress 'Expect: 100-continue' header, as some servers
150  // will reject it with a 417 and Curl won't auto retry
151  // with HTTP 1.0 fallback
152  $this->guzzleOptions['expect'] = false;
153  }
154 
155  $this->guzzleOptions['headers'] = $this->reqHeaders;
156 
157  if ( $this->handler ) {
158  $this->guzzleOptions['handler'] = $this->handler;
159  }
160 
161  if ( $this->sink ) {
162  $this->guzzleOptions['sink'] = $this->sink;
163  }
164 
165  if ( $this->caInfo ) {
166  $this->guzzleOptions['verify'] = $this->caInfo;
167  } elseif ( !$this->sslVerifyHost && !$this->sslVerifyCert ) {
168  $this->guzzleOptions['verify'] = false;
169  }
170 
171  try {
172  $client = new Client( $this->guzzleOptions );
173  $request = new Request( $this->method, $this->url );
174  $response = $client->send( $request );
175  $this->headerList = $response->getHeaders();
176 
177  $this->respVersion = $response->getProtocolVersion();
178  $this->respStatus = $response->getStatusCode() . ' ' . $response->getReasonPhrase();
179  } catch ( GuzzleHttp\Exception\ConnectException $e ) {
180  // ConnectException is thrown for several reasons besides generic "timeout":
181  // Connection refused
182  // couldn't connect to host
183  // connection attempt failed
184  // Could not resolve IPv4 address for host
185  // Could not resolve IPv6 address for host
186  if ( $this->usingCurl() ) {
187  $handlerContext = $e->getHandlerContext();
188  if ( $handlerContext['errno'] == CURLE_OPERATION_TIMEOUTED ) {
189  $this->status->fatal( 'http-timed-out', $this->url );
190  } else {
191  $this->status->fatal( 'http-curl-error', $handlerContext['error'] );
192  }
193  } else {
194  $this->status->fatal( 'http-request-error' );
195  }
196  } catch ( GuzzleHttp\Exception\RequestException $e ) {
197  if ( $this->usingCurl() ) {
198  $handlerContext = $e->getHandlerContext();
199  $this->status->fatal( 'http-curl-error', $handlerContext['error'] );
200  } else {
201  // Non-ideal, but the only way to identify connection timeout vs other conditions
202  $needle = 'Connection timed out';
203  if ( strpos( $e->getMessage(), $needle ) !== false ) {
204  $this->status->fatal( 'http-timed-out', $this->url );
205  } else {
206  $this->status->fatal( 'http-request-error' );
207  }
208  }
209  } catch ( GuzzleHttp\Exception\GuzzleException $e ) {
210  $this->status->fatal( 'http-internal-error' );
211  }
212 
213  if ( $this->profiler ) {
214  $profileSection = $this->profiler->scopedProfileIn(
215  __METHOD__ . '-' . $this->profileName
216  );
217  }
218 
219  if ( $this->profiler ) {
220  $this->profiler->scopedProfileOut( $profileSection );
221  }
222 
223  $this->parseHeader();
224  $this->setStatus();
225 
226  return Status::wrap( $this->status ); // TODO B/C; move this to callers
227  }
228 
229  protected function prepare() {
230  $this->doSetCallback( $this->callback );
231  parent::prepare();
232  }
233 
237  protected function usingCurl() {
238  return ( $this->handler && is_a( $this->handler, 'GuzzleHttp\Handler\CurlHandler' ) ) ||
239  ( !$this->handler && extension_loaded( 'curl' ) );
240  }
241 
246  protected function parseHeader() {
247  // Failure without (valid) headers gets a response status of zero
248  if ( !$this->status->isOK() ) {
249  $this->respStatus = '0 Error';
250  }
251 
252  foreach ( $this->headerList as $name => $values ) {
253  $this->respHeaders[strtolower( $name )] = $values;
254  }
255 
256  $this->parseCookies();
257  }
258 }
MWHttpRequest\$callback
callable $callback
Definition: MWHttpRequest.php:55
MWHttpRequest\setStatus
setStatus()
Sets HTTPRequest status member to a fatal value with the error message if the returned integer value ...
Definition: MWHttpRequest.php:436
$response
$response
Definition: opensearch_desc.php:38
MWHttpRequest\$maxRedirects
$maxRedirects
Definition: MWHttpRequest.php:56
GuzzleHttpRequest\$sink
$sink
Definition: GuzzleHttpRequest.php:43
MWHttpRequest\$profiler
Profiler $profiler
Definition: MWHttpRequest.php:77
GuzzleHttpRequest\parseHeader
parseHeader()
Guzzle provides headers as an array.
Definition: GuzzleHttpRequest.php:246
MWHttpRequest\$connectTimeout
$connectTimeout
Definition: MWHttpRequest.php:58
MWHttpRequest\$postData
$postData
Definition: MWHttpRequest.php:43
MWHttpRequest\parseCookies
parseCookies()
Parse the cookies in the response headers and store them in the cookie jar.
Definition: MWHttpRequest.php:568
Status\wrap
static wrap( $sv)
Succinct helper method to wrap a StatusValue.
Definition: Status.php:58
GuzzleHttpRequest
MWHttpRequest implemented using the Guzzle library.
Definition: GuzzleHttpRequest.php:39
GuzzleHttpRequest\prepare
prepare()
Definition: GuzzleHttpRequest.php:229
Profiler
Profiler base class that defines the interface and some trivial functionality.
Definition: Profiler.php:33
MWHttpRequest\$timeout
int string $timeout
Definition: MWHttpRequest.php:39
GuzzleHttpRequest\doSetCallback
doSetCallback( $callback)
Worker function for setting callbacks.
Definition: GuzzleHttpRequest.php:102
GuzzleHttpRequest\$handler
$handler
Definition: GuzzleHttpRequest.php:42
GuzzleHttpRequest\execute
execute()
Definition: GuzzleHttpRequest.php:114
MWHttpRequest
This wrapper class will call out to curl (if available) or fallback to regular PHP if necessary for h...
Definition: MWHttpRequest.php:33
MWHttpRequest\$reqHeaders
array $reqHeaders
Definition: MWHttpRequest.php:51
MWHttpRequest\$caInfo
$caInfo
Definition: MWHttpRequest.php:48
MWCallbackStream
Definition: MWCallbackStream.php:34
GuzzleHttpRequest\__construct
__construct( $url, array $options=[], $caller=__METHOD__, Profiler $profiler=null)
Definition: GuzzleHttpRequest.php:54
GuzzleHttpRequest\SUPPORTS_FILE_POSTS
const SUPPORTS_FILE_POSTS
Definition: GuzzleHttpRequest.php:40
GuzzleHttpRequest\setCallback
setCallback( $callback)
Set a read callback to accept data read from the HTTP request.
Definition: GuzzleHttpRequest.php:87
MWHttpRequest\$proxy
$proxy
Definition: MWHttpRequest.php:44
GuzzleHttpRequest\$guzzleOptions
array $guzzleOptions
Definition: GuzzleHttpRequest.php:45
GuzzleHttpRequest\usingCurl
usingCurl()
Definition: GuzzleHttpRequest.php:237
MWHttpRequest\$url
$url
Definition: MWHttpRequest.php:52