MediaWiki  master
CurlHttpRequest.php
Go to the documentation of this file.
1 <?php
25  public const SUPPORTS_FILE_POSTS = true;
26 
27  protected $curlOptions = [];
28  protected $headerText = "";
29 
34  public function __construct() {
35  if ( !function_exists( 'curl_init' ) ) {
36  throw new RuntimeException(
37  __METHOD__ . ': curl (https://www.php.net/curl) is not installed' );
38  }
39 
40  parent::__construct( ...func_get_args() );
41  }
42 
48  protected function readHeader( $fh, $content ) {
49  $this->headerText .= $content;
50  return strlen( $content );
51  }
52 
59  public function execute() {
60  $this->prepare();
61 
62  if ( !$this->status->isOK() ) {
63  return Status::wrap( $this->status ); // TODO B/C; move this to callers
64  }
65 
66  $this->curlOptions[CURLOPT_PROXY] = $this->proxy;
67  $this->curlOptions[CURLOPT_TIMEOUT] = $this->timeout;
68  $this->curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = $this->connectTimeout * 1000;
69  $this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
70  $this->curlOptions[CURLOPT_WRITEFUNCTION] = $this->callback;
71  $this->curlOptions[CURLOPT_HEADERFUNCTION] = [ $this, "readHeader" ];
72  $this->curlOptions[CURLOPT_MAXREDIRS] = $this->maxRedirects;
73  $this->curlOptions[CURLOPT_ENCODING] = ""; # Enable compression
74 
75  $this->curlOptions[CURLOPT_USERAGENT] = $this->reqHeaders['User-Agent'];
76 
77  $this->curlOptions[CURLOPT_SSL_VERIFYHOST] = $this->sslVerifyHost ? 2 : 0;
78  $this->curlOptions[CURLOPT_SSL_VERIFYPEER] = $this->sslVerifyCert;
79 
80  if ( $this->caInfo ) {
81  $this->curlOptions[CURLOPT_CAINFO] = $this->caInfo;
82  }
83 
84  if ( $this->headersOnly ) {
85  $this->curlOptions[CURLOPT_NOBODY] = true;
86  $this->curlOptions[CURLOPT_HEADER] = true;
87  } elseif ( $this->method == 'POST' ) {
88  $this->curlOptions[CURLOPT_POST] = true;
90  // Don't interpret POST parameters starting with '@' as file uploads, because this
91  // makes it impossible to POST plain values starting with '@' (and causes security
92  // issues potentially exposing the contents of local files).
93  $this->curlOptions[CURLOPT_SAFE_UPLOAD] = true;
94  $this->curlOptions[CURLOPT_POSTFIELDS] = $postData;
95 
96  // Suppress 'Expect: 100-continue' header, as some servers
97  // will reject it with a 417 and Curl won't auto retry
98  // with HTTP 1.0 fallback
99  $this->reqHeaders['Expect'] = '';
100  } else {
101  $this->curlOptions[CURLOPT_CUSTOMREQUEST] = $this->method;
102  }
103 
104  $this->curlOptions[CURLOPT_HTTPHEADER] = $this->getHeaderList();
105 
106  $curlHandle = curl_init( $this->url );
107 
108  if ( !curl_setopt_array( $curlHandle, $this->curlOptions ) ) {
109  $this->status->fatal( 'http-internal-error' );
110  throw new InvalidArgumentException( "Error setting curl options." );
111  }
112 
113  if ( $this->followRedirects && $this->canFollowRedirects() ) {
114  Wikimedia\suppressWarnings();
115  if ( !curl_setopt( $curlHandle, CURLOPT_FOLLOWLOCATION, true ) ) {
116  $this->logger->debug( __METHOD__ . ": Couldn't set CURLOPT_FOLLOWLOCATION. " .
117  "Probably open_basedir is set." );
118  // Continue the processing. If it were in curl_setopt_array,
119  // processing would have halted on its entry
120  }
121  Wikimedia\restoreWarnings();
122  }
123 
124  if ( $this->profiler ) {
125  $profileSection = $this->profiler->scopedProfileIn(
126  __METHOD__ . '-' . $this->profileName
127  );
128  }
129 
130  $curlRes = curl_exec( $curlHandle );
131  if ( curl_errno( $curlHandle ) == CURLE_OPERATION_TIMEOUTED ) {
132  $this->status->fatal( 'http-timed-out', $this->url );
133  } elseif ( $curlRes === false ) {
134  $this->status->fatal( 'http-curl-error', curl_error( $curlHandle ) );
135  } else {
136  $this->headerList = explode( "\r\n", $this->headerText );
137  }
138 
139  curl_close( $curlHandle );
140 
141  if ( $this->profiler ) {
142  $this->profiler->scopedProfileOut( $profileSection );
143  }
144 
145  $this->parseHeader();
146  $this->setStatus();
147 
148  return Status::wrap( $this->status ); // TODO B/C; move this to callers
149  }
150 
154  public function canFollowRedirects() {
155  $curlVersionInfo = curl_version();
156  if ( $curlVersionInfo['version_number'] < 0x071304 ) {
157  $this->logger->debug( "Cannot follow redirects with libcurl < 7.19.4 due to CVE-2009-0037" );
158  return false;
159  }
160 
161  return true;
162  }
163 }
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:442
MWHttpRequest\$maxRedirects
$maxRedirects
Definition: MWHttpRequest.php:56
MWHttpRequest\$sslVerifyCert
$sslVerifyCert
Definition: MWHttpRequest.php:47
MWHttpRequest\$content
$content
Definition: MWHttpRequest.php:41
CurlHttpRequest\$headerText
$headerText
Definition: CurlHttpRequest.php:28
CurlHttpRequest
MWHttpRequest implemented using internal curl compiled into PHP.
Definition: CurlHttpRequest.php:24
CurlHttpRequest\__construct
__construct()
Definition: CurlHttpRequest.php:34
MWHttpRequest\parseHeader
parseHeader()
Parses the headers, including the HTTP status code and any Set-Cookie headers.
Definition: MWHttpRequest.php:411
MWHttpRequest\$postData
$postData
Definition: MWHttpRequest.php:43
Status\wrap
static wrap( $sv)
Succinct helper method to wrap a StatusValue.
Definition: Status.php:62
MWHttpRequest\$timeout
int string $timeout
Definition: MWHttpRequest.php:39
CurlHttpRequest\execute
execute()
Definition: CurlHttpRequest.php:59
MWHttpRequest\$method
$method
Definition: MWHttpRequest.php:49
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\getHeaderList
getHeaderList()
Get an array of the headers.
Definition: MWHttpRequest.php:306
CurlHttpRequest\$curlOptions
$curlOptions
Definition: CurlHttpRequest.php:27
MWHttpRequest\$caInfo
$caInfo
Definition: MWHttpRequest.php:48
CurlHttpRequest\canFollowRedirects
canFollowRedirects()
Definition: CurlHttpRequest.php:154
CurlHttpRequest\SUPPORTS_FILE_POSTS
const SUPPORTS_FILE_POSTS
Definition: CurlHttpRequest.php:25
CurlHttpRequest\readHeader
readHeader( $fh, $content)
Definition: CurlHttpRequest.php:48
MWHttpRequest\$proxy
$proxy
Definition: MWHttpRequest.php:44
MWHttpRequest\prepare
prepare()
Definition: MWHttpRequest.php:387