public function execute() { wfProfileIn( __METHOD__ ); parent::execute(); if ( is_array( $this->postData ) ) { $this->postData = wfArrayToCgi( $this->postData ); } if ( $this->parsedUrl['scheme'] != 'http' && $this->parsedUrl['scheme'] != 'https' ) { $this->status->fatal( 'http-invalid-scheme', $this->parsedUrl['scheme'] ); } $this->reqHeaders['Accept'] = "*/*"; if ( $this->method == 'POST' ) { // Required for HTTP 1.0 POSTs $this->reqHeaders['Content-Length'] = strlen( $this->postData ); if ( !isset( $this->reqHeaders['Content-Type'] ) ) { $this->reqHeaders['Content-Type'] = "application/x-www-form-urlencoded"; } } $options = array(); if ( $this->proxy ) { $options['proxy'] = $this->urlToTCP( $this->proxy ); $options['request_fulluri'] = true; } if ( !$this->followRedirects ) { $options['max_redirects'] = 0; } else { $options['max_redirects'] = $this->maxRedirects; } $options['method'] = $this->method; $options['header'] = implode( "\r\n", $this->getHeaderList() ); // Note that at some future point we may want to support // HTTP/1.1, but we'd have to write support for chunking // in version of PHP < 5.3.1 $options['protocol_version'] = "1.0"; // This is how we tell PHP we want to deal with 404s (for example) ourselves. // Only works on 5.2.10+ $options['ignore_errors'] = true; if ( $this->postData ) { $options['content'] = $this->postData; } $options['timeout'] = $this->timeout; if ( $this->sslVerifyHost ) { $options['CN_match'] = $this->parsedUrl['host']; } if ( $this->sslVerifyCert ) { $options['verify_peer'] = true; } if ( is_dir( $this->caInfo ) ) { $options['capath'] = $this->caInfo; } elseif ( is_file( $this->caInfo ) ) { $options['cafile'] = $this->caInfo; } elseif ( $this->caInfo ) { throw new MWException( "Invalid CA info passed: {$this->caInfo}" ); } $scheme = $this->parsedUrl['scheme']; $context = stream_context_create( array( "$scheme" => $options ) ); $this->headerList = array(); $reqCount = 0; $url = $this->url; $result = array(); do { $reqCount++; wfSuppressWarnings(); $fh = fopen( $url, "r", false, $context ); wfRestoreWarnings(); if ( !$fh ) { break; } $result = stream_get_meta_data( $fh ); $this->headerList = $result['wrapper_data']; $this->parseHeader(); if ( !$this->followRedirects ) { break; } # Handle manual redirection if ( !$this->isRedirect() || $reqCount > $this->maxRedirects ) { break; } # Check security of URL $url = $this->getResponseHeader( "Location" ); if ( !Http::isValidURI( $url ) ) { wfDebug( __METHOD__ . ": insecure redirection\n" ); break; } } while ( true ); $this->setStatus(); if ( $fh === false ) { $this->status->fatal( 'http-request-error' ); wfProfileOut( __METHOD__ ); return $this->status; } if ( $result['timed_out'] ) { $this->status->fatal( 'http-timed-out', $this->url ); wfProfileOut( __METHOD__ ); return $this->status; } // If everything went OK, or we received some error code // get the response body content. if ( $this->status->isOK() || (int)$this->respStatus >= 300 ) { while ( !feof( $fh ) ) { $buf = fread( $fh, 8192 ); if ( $buf === false ) { $this->status->fatal( 'http-read-error' ); break; } if ( strlen( $buf ) ) { call_user_func( $this->callback, $fh, $buf ); } } } fclose( $fh ); wfProfileOut( __METHOD__ ); return $this->status; }
public function execute() { parent::execute(); if (is_array($this->postData)) { $this->postData = wfArrayToCgi($this->postData); } if ($this->parsedUrl['scheme'] != 'http' && $this->parsedUrl['scheme'] != 'https') { $this->status->fatal('http-invalid-scheme', $this->parsedUrl['scheme']); } $this->reqHeaders['Accept'] = "*/*"; $this->reqHeaders['Connection'] = 'Close'; if ($this->method == 'POST') { // Required for HTTP 1.0 POSTs $this->reqHeaders['Content-Length'] = strlen($this->postData); if (!isset($this->reqHeaders['Content-Type'])) { $this->reqHeaders['Content-Type'] = "application/x-www-form-urlencoded"; } } // Set up PHP stream context $options = ['http' => ['method' => $this->method, 'header' => implode("\r\n", $this->getHeaderList()), 'protocol_version' => '1.1', 'max_redirects' => $this->followRedirects ? $this->maxRedirects : 0, 'ignore_errors' => true, 'timeout' => $this->timeout, 'curl_verify_ssl_host' => $this->sslVerifyHost ? 2 : 0, 'curl_verify_ssl_peer' => $this->sslVerifyCert], 'ssl' => ['verify_peer' => $this->sslVerifyCert, 'SNI_enabled' => true, 'ciphers' => 'HIGH:!SSLv2:!SSLv3:-ADH:-kDH:-kECDH:-DSS', 'disable_compression' => true]]; if ($this->proxy) { $options['http']['proxy'] = $this->urlToTcp($this->proxy); $options['http']['request_fulluri'] = true; } if ($this->postData) { $options['http']['content'] = $this->postData; } if ($this->sslVerifyHost) { // PHP 5.6.0 deprecates CN_match, in favour of peer_name which // actually checks SubjectAltName properly. if (version_compare(PHP_VERSION, '5.6.0', '>=')) { $options['ssl']['peer_name'] = $this->parsedUrl['host']; } else { $options['ssl']['CN_match'] = $this->parsedUrl['host']; } } $options['ssl'] += $this->getCertOptions(); $context = stream_context_create($options); $this->headerList = []; $reqCount = 0; $url = $this->url; $result = []; if ($this->profiler) { $profileSection = $this->profiler->scopedProfileIn(__METHOD__ . '-' . $this->profileName); } do { $reqCount++; $this->fopenErrors = []; set_error_handler([$this, 'errorHandler']); $fh = fopen($url, "r", false, $context); restore_error_handler(); if (!$fh) { // HACK for instant commons. // If we are contacting (commons|upload).wikimedia.org // try again with CN_match for en.wikipedia.org // as php does not handle SubjectAltName properly // prior to "peer_name" option in php 5.6 if (isset($options['ssl']['CN_match']) && ($options['ssl']['CN_match'] === 'commons.wikimedia.org' || $options['ssl']['CN_match'] === 'upload.wikimedia.org')) { $options['ssl']['CN_match'] = 'en.wikipedia.org'; $context = stream_context_create($options); continue; } break; } $result = stream_get_meta_data($fh); $this->headerList = $result['wrapper_data']; $this->parseHeader(); if (!$this->followRedirects) { break; } # Handle manual redirection if (!$this->isRedirect() || $reqCount > $this->maxRedirects) { break; } # Check security of URL $url = $this->getResponseHeader("Location"); if (!Http::isValidURI($url)) { $this->logger->debug(__METHOD__ . ": insecure redirection\n"); break; } } while (true); if ($this->profiler) { $this->profiler->scopedProfileOut($profileSection); } $this->setStatus(); if ($fh === false) { if ($this->fopenErrors) { $this->logger->warning(__CLASS__ . ': error opening connection: {errstr1}', $this->fopenErrors); } $this->status->fatal('http-request-error'); return $this->status; } if ($result['timed_out']) { $this->status->fatal('http-timed-out', $this->url); return $this->status; } // If everything went OK, or we received some error code // get the response body content. if ($this->status->isOK() || (int) $this->respStatus >= 300) { while (!feof($fh)) { $buf = fread($fh, 8192); if ($buf === false) { $this->status->fatal('http-read-error'); break; } if (strlen($buf)) { call_user_func($this->callback, $fh, $buf); } } } fclose($fh); return $this->status; }
public function execute() { wfProfileIn(__METHOD__); parent::execute(); if (is_array($this->postData)) { $this->postData = wfArrayToCgi($this->postData); } if ($this->parsedUrl['scheme'] != 'http' && $this->parsedUrl['scheme'] != 'https') { $this->status->fatal('http-invalid-scheme', $this->parsedUrl['scheme']); } $this->reqHeaders['Accept'] = "*/*"; $this->reqHeaders['Connection'] = 'Close'; if ($this->method == 'POST') { // Required for HTTP 1.0 POSTs $this->reqHeaders['Content-Length'] = strlen($this->postData); if (!isset($this->reqHeaders['Content-Type'])) { $this->reqHeaders['Content-Type'] = "application/x-www-form-urlencoded"; } } // Set up PHP stream context $options = array('http' => array('method' => $this->method, 'header' => implode("\r\n", $this->getHeaderList()), 'protocol_version' => '1.1', 'max_redirects' => $this->followRedirects ? $this->maxRedirects : 0, 'ignore_errors' => true, 'timeout' => $this->timeout, 'curl_verify_ssl_host' => $this->sslVerifyHost ? 2 : 0, 'curl_verify_ssl_peer' => $this->sslVerifyCert), 'ssl' => array('verify_peer' => $this->sslVerifyCert, 'SNI_enabled' => true)); if ($this->proxy) { $options['http']['proxy'] = $this->urlToTCP($this->proxy); $options['http']['request_fulluri'] = true; } if ($this->postData) { $options['http']['content'] = $this->postData; } if ($this->sslVerifyHost) { $options['ssl']['CN_match'] = $this->parsedUrl['host']; } if (is_dir($this->caInfo)) { $options['ssl']['capath'] = $this->caInfo; } elseif (is_file($this->caInfo)) { $options['ssl']['cafile'] = $this->caInfo; } elseif ($this->caInfo) { throw new MWException("Invalid CA info passed: {$this->caInfo}"); } $context = stream_context_create($options); $this->headerList = array(); $reqCount = 0; $url = $this->url; $result = array(); do { $reqCount++; wfSuppressWarnings(); $fh = fopen($url, "r", false, $context); wfRestoreWarnings(); if (!$fh) { break; } $result = stream_get_meta_data($fh); $this->headerList = $result['wrapper_data']; $this->parseHeader(); if (!$this->followRedirects) { break; } # Handle manual redirection if (!$this->isRedirect() || $reqCount > $this->maxRedirects) { break; } # Check security of URL $url = $this->getResponseHeader("Location"); if (!Http::isValidURI($url)) { wfDebug(__METHOD__ . ": insecure redirection\n"); break; } } while (true); $this->setStatus(); if ($fh === false) { $this->status->fatal('http-request-error'); wfProfileOut(__METHOD__); return $this->status; } if ($result['timed_out']) { $this->status->fatal('http-timed-out', $this->url); wfProfileOut(__METHOD__); return $this->status; } // If everything went OK, or we received some error code // get the response body content. if ($this->status->isOK() || (int) $this->respStatus >= 300) { while (!feof($fh)) { $buf = fread($fh, 8192); if ($buf === false) { $this->status->fatal('http-read-error'); break; } if (strlen($buf)) { call_user_func($this->callback, $fh, $buf); } } } fclose($fh); wfProfileOut(__METHOD__); return $this->status; }
public function execute() { parent::execute(); if (!$this->status->isOK()) { return $this->status; } $this->curlOptions[CURLOPT_PROXY] = $this->proxy; $this->curlOptions[CURLOPT_TIMEOUT] = $this->timeout; // Only supported in curl >= 7.16.2 if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { $this->curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = $this->connectTimeout * 1000; } $this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; $this->curlOptions[CURLOPT_WRITEFUNCTION] = $this->callback; $this->curlOptions[CURLOPT_HEADERFUNCTION] = [$this, "readHeader"]; $this->curlOptions[CURLOPT_MAXREDIRS] = $this->maxRedirects; $this->curlOptions[CURLOPT_ENCODING] = ""; # Enable compression $this->curlOptions[CURLOPT_USERAGENT] = $this->reqHeaders['User-Agent']; $this->curlOptions[CURLOPT_SSL_VERIFYHOST] = $this->sslVerifyHost ? 2 : 0; $this->curlOptions[CURLOPT_SSL_VERIFYPEER] = $this->sslVerifyCert; if ($this->caInfo) { $this->curlOptions[CURLOPT_CAINFO] = $this->caInfo; } if ($this->headersOnly) { $this->curlOptions[CURLOPT_NOBODY] = true; $this->curlOptions[CURLOPT_HEADER] = true; } elseif ($this->method == 'POST') { $this->curlOptions[CURLOPT_POST] = true; $postData = $this->postData; // Don't interpret POST parameters starting with '@' as file uploads, because this // makes it impossible to POST plain values starting with '@' (and causes security // issues potentially exposing the contents of local files). // The PHP manual says this option was introduced in PHP 5.5 defaults to true in PHP 5.6, // but we support lower versions, and the option doesn't exist in HHVM 5.6.99. if (defined('CURLOPT_SAFE_UPLOAD')) { $this->curlOptions[CURLOPT_SAFE_UPLOAD] = true; } elseif (is_array($postData)) { // In PHP 5.2 and later, '@' is interpreted as a file upload if POSTFIELDS // is an array, but not if it's a string. So convert $req['body'] to a string // for safety. $postData = wfArrayToCgi($postData); } $this->curlOptions[CURLOPT_POSTFIELDS] = $postData; // Suppress 'Expect: 100-continue' header, as some servers // will reject it with a 417 and Curl won't auto retry // with HTTP 1.0 fallback $this->reqHeaders['Expect'] = ''; } else { $this->curlOptions[CURLOPT_CUSTOMREQUEST] = $this->method; } $this->curlOptions[CURLOPT_HTTPHEADER] = $this->getHeaderList(); $curlHandle = curl_init($this->url); if (!curl_setopt_array($curlHandle, $this->curlOptions)) { throw new MWException("Error setting curl options."); } if ($this->followRedirects && $this->canFollowRedirects()) { MediaWiki\suppressWarnings(); if (!curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, true)) { $this->logger->debug(__METHOD__ . ": Couldn't set CURLOPT_FOLLOWLOCATION. " . "Probably open_basedir is set.\n"); // Continue the processing. If it were in curl_setopt_array, // processing would have halted on its entry } MediaWiki\restoreWarnings(); } if ($this->profiler) { $profileSection = $this->profiler->scopedProfileIn(__METHOD__ . '-' . $this->profileName); } $curlRes = curl_exec($curlHandle); if (curl_errno($curlHandle) == CURLE_OPERATION_TIMEOUTED) { $this->status->fatal('http-timed-out', $this->url); } elseif ($curlRes === false) { $this->status->fatal('http-curl-error', curl_error($curlHandle)); } else { $this->headerList = explode("\r\n", $this->headerText); } curl_close($curlHandle); if ($this->profiler) { $this->profiler->scopedProfileOut($profileSection); } $this->parseHeader(); $this->setStatus(); return $this->status; }
public function execute() { parent::execute(); // At least on Centos 4.8 with PHP 5.1.6, using max_redirects to follow redirects // causes a segfault $manuallyRedirect = version_compare(phpversion(), '5.1.7', '<'); if ($this->parsedUrl['scheme'] != 'http' && $this->parsedUrl['scheme'] != 'https') { $this->status->fatal('http-invalid-scheme', $this->parsedUrl['scheme']); } $this->reqHeaders['Accept'] = "*/*"; if ($this->method == 'POST') { // Required for HTTP 1.0 POSTs $this->reqHeaders['Content-Length'] = strlen($this->postData); $this->reqHeaders['Content-type'] = "application/x-www-form-urlencoded"; } $options = array(); if ($this->proxy && !$this->noProxy) { $options['proxy'] = $this->urlToTCP($this->proxy); $options['request_fulluri'] = true; } if (!$this->followRedirects || $manuallyRedirect) { $options['max_redirects'] = 0; } else { $options['max_redirects'] = $this->maxRedirects; } $options['method'] = $this->method; $options['header'] = implode("\r\n", $this->getHeaderList()); // Note that at some future point we may want to support // HTTP/1.1, but we'd have to write support for chunking // in version of PHP < 5.3.1 $options['protocol_version'] = "1.0"; // This is how we tell PHP we want to deal with 404s (for example) ourselves. // Only works on 5.2.10+ $options['ignore_errors'] = true; if ($this->postData) { $options['content'] = $this->postData; } $oldTimeout = false; if (version_compare('5.2.1', phpversion(), '>')) { $oldTimeout = ini_set('default_socket_timeout', $this->timeout); } else { $options['timeout'] = $this->timeout; } $context = stream_context_create(array('http' => $options)); $this->headerList = array(); $reqCount = 0; $url = $this->url; do { $reqCount++; wfSuppressWarnings(); $fh = fopen($url, "r", false, $context); wfRestoreWarnings(); if (!$fh) { break; } $result = stream_get_meta_data($fh); $this->headerList = $result['wrapper_data']; $this->parseHeader(); if (!$manuallyRedirect || !$this->followRedirects) { break; } # Handle manual redirection if (!$this->isRedirect() || $reqCount > $this->maxRedirects) { break; } # Check security of URL $url = $this->getResponseHeader("Location"); if (substr($url, 0, 7) !== 'http://') { wfDebug(__METHOD__ . ": insecure redirection\n"); break; } } while (true); if ($oldTimeout !== false) { ini_set('default_socket_timeout', $oldTimeout); } $this->setStatus(); if ($fh === false) { $this->status->fatal('http-request-error'); return $this->status; } if ($result['timed_out']) { $this->status->fatal('http-timed-out', $this->url); return $this->status; } if ($this->status->isOK()) { while (!feof($fh)) { $buf = fread($fh, 8192); if ($buf === false) { $this->status->fatal('http-read-error'); break; } if (strlen($buf)) { call_user_func($this->callback, $fh, $buf); } } } fclose($fh); return $this->status; }