MediaWiki REL1_32
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 $options['ssl']['peer_name'] = $this->parsedUrl['host'];
141 }
142
143 $options['ssl'] += $this->getCertOptions();
144
145 $context = stream_context_create( $options );
146
147 $this->headerList = [];
148 $reqCount = 0;
149 $url = $this->url;
150
151 $result = [];
152
153 if ( $this->profiler ) {
154 $profileSection = $this->profiler->scopedProfileIn(
155 __METHOD__ . '-' . $this->profileName
156 );
157 }
158 do {
159 $reqCount++;
160 $this->fopenErrors = [];
161 set_error_handler( [ $this, 'errorHandler' ] );
162 $fh = fopen( $url, "r", false, $context );
163 restore_error_handler();
164
165 if ( !$fh ) {
166 break;
167 }
168
169 $result = stream_get_meta_data( $fh );
170 $this->headerList = $result['wrapper_data'];
171 $this->parseHeader();
172
173 if ( !$this->followRedirects ) {
174 break;
175 }
176
177 # Handle manual redirection
178 if ( !$this->isRedirect() || $reqCount > $this->maxRedirects ) {
179 break;
180 }
181 # Check security of URL
182 $url = $this->getResponseHeader( "Location" );
183
184 if ( !Http::isValidURI( $url ) ) {
185 $this->logger->debug( __METHOD__ . ": insecure redirection\n" );
186 break;
187 }
188 } while ( true );
189 if ( $this->profiler ) {
190 $this->profiler->scopedProfileOut( $profileSection );
191 }
192
193 $this->setStatus();
194
195 if ( $fh === false ) {
196 if ( $this->fopenErrors ) {
197 $this->logger->warning( __CLASS__
198 . ': error opening connection: {errstr1}', $this->fopenErrors );
199 }
200 $this->status->fatal( 'http-request-error' );
201 return Status::wrap( $this->status ); // TODO B/C; move this to callers
202 }
203
204 if ( $result['timed_out'] ) {
205 $this->status->fatal( 'http-timed-out', $this->url );
206 return Status::wrap( $this->status ); // TODO B/C; move this to callers
207 }
208
209 // If everything went OK, or we received some error code
210 // get the response body content.
211 if ( $this->status->isOK() || (int)$this->respStatus >= 300 ) {
212 while ( !feof( $fh ) ) {
213 $buf = fread( $fh, 8192 );
214
215 if ( $buf === false ) {
216 $this->status->fatal( 'http-read-error' );
217 break;
218 }
219
220 if ( strlen( $buf ) ) {
221 call_user_func( $this->callback, $fh, $buf );
222 }
223 }
224 }
225 fclose( $fh );
226
227 return Status::wrap( $this->status ); // TODO B/C; move this to callers
228 }
229}
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:2050
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:2885