MediaWiki REL1_31
PhpHttpRequest.php
Go to the documentation of this file.
1<?php
22
23 private $fopenErrors = [];
24
29 protected function urlToTcp( $url ) {
30 $parsedUrl = parse_url( $url );
31
32 return 'tcp://' . $parsedUrl['host'] . ':' . $parsedUrl['port'];
33 }
34
44 protected function getCertOptions() {
45 $certOptions = [];
46 $certLocations = [];
47 if ( $this->caInfo ) {
48 $certLocations = [ 'manual' => $this->caInfo ];
49 }
50
51 foreach ( $certLocations as $key => $cert ) {
52 if ( is_dir( $cert ) ) {
53 $certOptions['capath'] = $cert;
54 break;
55 } elseif ( is_file( $cert ) ) {
56 $certOptions['cafile'] = $cert;
57 break;
58 } elseif ( $key === 'manual' ) {
59 // fail more loudly if a cert path was manually configured and it is not valid
60 throw new DomainException( "Invalid CA info passed: $cert" );
61 }
62 }
63
64 return $certOptions;
65 }
66
77 public function errorHandler( $errno, $errstr ) {
78 $n = count( $this->fopenErrors ) + 1;
79 $this->fopenErrors += [ "errno$n" => $errno, "errstr$n" => $errstr ];
80 }
81
87 public function execute() {
88 $this->prepare();
89
90 if ( is_array( $this->postData ) ) {
91 $this->postData = wfArrayToCgi( $this->postData );
92 }
93
94 if ( $this->parsedUrl['scheme'] != 'http'
95 && $this->parsedUrl['scheme'] != 'https' ) {
96 $this->status->fatal( 'http-invalid-scheme', $this->parsedUrl['scheme'] );
97 }
98
99 $this->reqHeaders['Accept'] = "*/*";
100 $this->reqHeaders['Connection'] = 'Close';
101 if ( $this->method == 'POST' ) {
102 // Required for HTTP 1.0 POSTs
103 $this->reqHeaders['Content-Length'] = strlen( $this->postData );
104 if ( !isset( $this->reqHeaders['Content-Type'] ) ) {
105 $this->reqHeaders['Content-Type'] = "application/x-www-form-urlencoded";
106 }
107 }
108
109 // Set up PHP stream context
110 $options = [
111 'http' => [
112 'method' => $this->method,
113 'header' => implode( "\r\n", $this->getHeaderList() ),
114 'protocol_version' => '1.1',
115 'max_redirects' => $this->followRedirects ? $this->maxRedirects : 0,
116 'ignore_errors' => true,
117 'timeout' => $this->timeout,
118 // Curl options in case curlwrappers are installed
119 'curl_verify_ssl_host' => $this->sslVerifyHost ? 2 : 0,
120 'curl_verify_ssl_peer' => $this->sslVerifyCert,
121 ],
122 'ssl' => [
123 'verify_peer' => $this->sslVerifyCert,
124 'SNI_enabled' => true,
125 'ciphers' => 'HIGH:!SSLv2:!SSLv3:-ADH:-kDH:-kECDH:-DSS',
126 'disable_compression' => true,
127 ],
128 ];
129
130 if ( $this->proxy ) {
131 $options['http']['proxy'] = $this->urlToTcp( $this->proxy );
132 $options['http']['request_fulluri'] = true;
133 }
134
135 if ( $this->postData ) {
136 $options['http']['content'] = $this->postData;
137 }
138
139 if ( $this->sslVerifyHost ) {
140 // PHP 5.6.0 deprecates CN_match, in favour of peer_name which
141 // actually checks SubjectAltName properly.
142 if ( version_compare( PHP_VERSION, '5.6.0', '>=' ) ) {
143 $options['ssl']['peer_name'] = $this->parsedUrl['host'];
144 } else {
145 $options['ssl']['CN_match'] = $this->parsedUrl['host'];
146 }
147 }
148
149 $options['ssl'] += $this->getCertOptions();
150
151 $context = stream_context_create( $options );
152
153 $this->headerList = [];
154 $reqCount = 0;
155 $url = $this->url;
156
157 $result = [];
158
159 if ( $this->profiler ) {
160 $profileSection = $this->profiler->scopedProfileIn(
161 __METHOD__ . '-' . $this->profileName
162 );
163 }
164 do {
165 $reqCount++;
166 $this->fopenErrors = [];
167 set_error_handler( [ $this, 'errorHandler' ] );
168 $fh = fopen( $url, "r", false, $context );
169 restore_error_handler();
170
171 if ( !$fh ) {
172 // HACK for instant commons.
173 // If we are contacting (commons|upload).wikimedia.org
174 // try again with CN_match for en.wikipedia.org
175 // as php does not handle SubjectAltName properly
176 // prior to "peer_name" option in php 5.6
177 if ( isset( $options['ssl']['CN_match'] )
178 && ( $options['ssl']['CN_match'] === 'commons.wikimedia.org'
179 || $options['ssl']['CN_match'] === 'upload.wikimedia.org' )
180 ) {
181 $options['ssl']['CN_match'] = 'en.wikipedia.org';
182 $context = stream_context_create( $options );
183 continue;
184 }
185 break;
186 }
187
188 $result = stream_get_meta_data( $fh );
189 $this->headerList = $result['wrapper_data'];
190 $this->parseHeader();
191
192 if ( !$this->followRedirects ) {
193 break;
194 }
195
196 # Handle manual redirection
197 if ( !$this->isRedirect() || $reqCount > $this->maxRedirects ) {
198 break;
199 }
200 # Check security of URL
201 $url = $this->getResponseHeader( "Location" );
202
203 if ( !Http::isValidURI( $url ) ) {
204 $this->logger->debug( __METHOD__ . ": insecure redirection\n" );
205 break;
206 }
207 } while ( true );
208 if ( $this->profiler ) {
209 $this->profiler->scopedProfileOut( $profileSection );
210 }
211
212 $this->setStatus();
213
214 if ( $fh === false ) {
215 if ( $this->fopenErrors ) {
216 $this->logger->warning( __CLASS__
217 . ': error opening connection: {errstr1}', $this->fopenErrors );
218 }
219 $this->status->fatal( 'http-request-error' );
220 return Status::wrap( $this->status ); // TODO B/C; move this to callers
221 }
222
223 if ( $result['timed_out'] ) {
224 $this->status->fatal( 'http-timed-out', $this->url );
225 return Status::wrap( $this->status ); // TODO B/C; move this to callers
226 }
227
228 // If everything went OK, or we received some error code
229 // get the response body content.
230 if ( $this->status->isOK() || (int)$this->respStatus >= 300 ) {
231 while ( !feof( $fh ) ) {
232 $buf = fread( $fh, 8192 );
233
234 if ( $buf === false ) {
235 $this->status->fatal( 'http-read-error' );
236 break;
237 }
238
239 if ( strlen( $buf ) ) {
240 call_user_func( $this->callback, $fh, $buf );
241 }
242 }
243 }
244 fclose( $fh );
245
246 return Status::wrap( $this->status ); // TODO B/C; move this to callers
247 }
248}
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
static prepare( $text, $lang)
Backward-compatibility shim for extensions.
static isValidURI( $uri)
Checks that the given URI is a valid one.
Definition Http.php:146
This wrapper class will call out to curl (if available) or fallback to regular PHP if necessary for h...
isRedirect()
Returns true if the last status code was a redirect.
setStatus()
Sets HTTPRequest status member to a fatal value with the error message if the returned integer value ...
parseHeader()
Parses the headers, including the HTTP status code and any Set-Cookie headers.
getResponseHeader( $header)
Returns the value of the given response header.
getHeaderList()
Get an array of the headers.
getCertOptions()
Returns an array with a 'capath' or 'cafile' key that is suitable to be merged into the 'ssl' sub-arr...
errorHandler( $errno, $errstr)
Custom error handler for dealing with fopen() errors.
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition hooks.txt:2001
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition hooks.txt:2811