61 wfDebug(
"HTTP: $method: $url\n" );
64 $options[
'method'] = strtoupper( $method );
66 if ( !isset(
$options[
'timeout'] ) ) {
69 if ( !isset(
$options[
'connectTimeout'] ) ) {
70 $options[
'connectTimeout'] =
'default';
74 $status = $req->execute();
77 if ( $status->isOK() ) {
78 $content = $req->getContent();
93 public static function get( $url, $timeout =
'default',
$options =
array() ) {
125 if ( preg_match(
'!^http://([\w.-]+)[/:].*$!', $url,
$matches ) ) {
128 $domainParts = explode(
'.', $host );
130 $domainParts = array_reverse( $domainParts );
133 $countParts = count( $domainParts );
134 for ( $i = 0; $i < $countParts; $i++ ) {
135 $domainPart = $domainParts[$i];
137 $domain = $domainPart;
139 $domain = $domainPart .
'.' . $domain;
142 if ( $wgConf->isLocalVHost( $domain ) ) {
157 return "MediaWiki/$wgVersion";
174 '/^https?:\/\/[^\/\s]\S*$/D',
224 global $wgHTTPTimeout, $wgHTTPConnectTimeout;
236 $this->timeout =
$options[
'timeout'];
238 $this->timeout = $wgHTTPTimeout;
240 if ( isset(
$options[
'connectTimeout'] ) &&
$options[
'connectTimeout'] !=
'default' ) {
241 $this->connectTimeout =
$options[
'connectTimeout'];
243 $this->connectTimeout = $wgHTTPConnectTimeout;
245 if ( isset(
$options[
'userAgent'] ) ) {
249 $members =
array(
"postData",
"proxy",
"noProxy",
"sslVerifyHost",
"caInfo",
250 "method",
"followRedirects",
"maxRedirects",
"sslVerifyCert",
"callback" );
252 foreach ( $members
as $o ) {
256 if ( $o ==
'method' ) {
263 if ( $this->noProxy ) {
274 return function_exists(
'curl_init' ) ||
wfIniGetBool(
'allow_url_fopen' );
289 throw new MWException( __METHOD__ .
': curl (http://php.net/curl) is not installed, but' .
290 ' Http::$httpEngine is set to "curl"' );
298 throw new MWException( __METHOD__ .
': allow_url_fopen ' .
299 'needs to be enabled for pure PHP http requests to ' .
300 'work. If possible, curl should be used instead. See ' .
301 'http://php.net/curl.'
306 throw new MWException( __METHOD__ .
': The setting of Http::$httpEngine is not valid.' );
326 $this->postData =
$args;
338 if ( $this->proxy && !$this->noProxy ) {
346 } elseif ( $wgHTTPProxy ) {
347 $this->proxy = $wgHTTPProxy;
348 } elseif ( getenv(
"http_proxy" ) ) {
349 $this->proxy = getenv(
"http_proxy" );
378 if ( $this->cookieJar ) {
379 $this->reqHeaders[
'Cookie'] =
380 $this->cookieJar->serializeToHttpRequest(
381 $this->parsedUrl[
'path'],
382 $this->parsedUrl[
'host']
387 $list[] =
"$name: $value";
413 throw new MWException(
'Invalid MwHttpRequest callback' );
441 if ( strtoupper( $this->method ) ==
"HEAD" ) {
442 $this->headersOnly =
true;
447 if ( !$this->callback ) {
451 if ( !isset( $this->reqHeaders[
'User-Agent'] ) ) {
468 foreach ( $this->headerList
as $header ) {
469 if ( preg_match(
"#^HTTP/([0-9.]+) (.*)#", $header, $match ) ) {
470 $this->respVersion = $match[1];
471 $this->respStatus = $match[2];
472 } elseif ( preg_match(
"#^[ \t]#", $header ) ) {
473 $last = count( $this->respHeaders[$lastname] ) - 1;
474 $this->respHeaders[$lastname][
$last] .=
"\r\n$header";
475 } elseif ( preg_match(
"#^([^:]*):[\t ]*(.*)#", $header, $match ) ) {
476 $this->respHeaders[strtolower( $match[1] )][] = $match[2];
477 $lastname = strtolower( $match[1] );
495 if ( !$this->respHeaders ) {
499 if ( (
int)$this->respStatus > 399 ) {
500 list( $code, $message ) = explode(
" ", $this->respStatus, 2 );
501 $this->status->fatal(
"http-bad-status", $code, $message );
513 if ( !$this->respHeaders ) {
526 if ( !$this->respHeaders ) {
530 $status = (int)$this->respStatus;
548 if ( !$this->respHeaders ) {
562 if ( !$this->respHeaders ) {
566 if ( isset( $this->respHeaders[strtolower( $header )] ) ) {
567 $v = $this->respHeaders[strtolower( $header )];
568 return $v[count( $v ) - 1];
580 $this->cookieJar = $jar;
589 if ( !$this->respHeaders ) {
606 if ( !$this->cookieJar ) {
619 if ( !$this->cookieJar ) {
623 if ( isset( $this->respHeaders[
'set-cookie'] ) ) {
625 foreach ( $this->respHeaders[
'set-cookie']
as $cookie ) {
626 $this->cookieJar->parseCookieResponseHeader( $cookie,
$url[
'host'] );
653 if ( isset( $headers[
'location'] ) ) {
654 $locations = $headers[
'location'];
656 $foundRelativeURI =
false;
657 $countLocations = count( $locations );
659 for ( $i = $countLocations - 1; $i >= 0; $i-- ) {
660 $url = parse_url( $locations[$i] );
662 if ( isset(
$url[
'host'] ) ) {
663 $domain =
$url[
'scheme'] .
'://' .
$url[
'host'];
666 $foundRelativeURI =
true;
670 if ( $foundRelativeURI ) {
672 return $domain . $locations[$countLocations - 1];
674 $url = parse_url( $this->url );
675 if ( isset(
$url[
'host'] ) ) {
676 return $url[
'scheme'] .
'://' .
$url[
'host'] .
677 $locations[$countLocations - 1];
681 return $locations[$countLocations - 1];
722 if ( !$this->status->isOK() ) {
731 if ( defined(
'CURLOPT_CONNECTTIMEOUT_MS' ) ) {
732 $this->curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = $this->connectTimeout * 1000;
735 $this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
737 $this->curlOptions[CURLOPT_HEADERFUNCTION] =
array( $this,
"readHeader" );
739 $this->curlOptions[CURLOPT_ENCODING] =
""; # Enable compression
741 $this->curlOptions[CURLOPT_USERAGENT] = $this->reqHeaders[
'User-Agent'];
743 $this->curlOptions[CURLOPT_SSL_VERIFYHOST] = $this->sslVerifyHost ? 2 : 0;
746 if ( $this->caInfo ) {
750 if ( $this->headersOnly ) {
751 $this->curlOptions[CURLOPT_NOBODY] =
true;
752 $this->curlOptions[CURLOPT_HEADER] =
true;
753 } elseif ( $this->method ==
'POST' ) {
754 $this->curlOptions[CURLOPT_POST] =
true;
759 $this->reqHeaders[
'Expect'] =
'';
764 $this->curlOptions[CURLOPT_HTTPHEADER] = $this->
getHeaderList();
766 $curlHandle = curl_init( $this->url );
768 if ( !curl_setopt_array( $curlHandle, $this->curlOptions ) ) {
770 throw new MWException(
"Error setting curl options." );
775 if ( ! curl_setopt( $curlHandle, CURLOPT_FOLLOWLOCATION,
true ) ) {
776 wfDebug( __METHOD__ .
": Couldn't set CURLOPT_FOLLOWLOCATION. " .
777 "Probably safe_mode or open_basedir is set.\n" );
784 $curlRes = curl_exec( $curlHandle );
785 if ( curl_errno( $curlHandle ) == CURLE_OPERATION_TIMEOUTED ) {
786 $this->status->fatal(
'http-timed-out', $this->url );
787 } elseif ( $curlRes ===
false ) {
788 $this->status->fatal(
'http-curl-error', curl_error( $curlHandle ) );
790 $this->headerList = explode(
"\r\n", $this->headerText );
793 curl_close( $curlHandle );
807 if ( strval( ini_get(
'open_basedir' ) ) !==
'' ||
wfIniGetBool(
'safe_mode' ) ) {
808 wfDebug(
"Cannot follow redirects in safe mode\n" );
812 if ( !defined(
'CURLOPT_REDIR_PROTOCOLS' ) ) {
813 wfDebug(
"Cannot follow redirects with libcurl < 7.19.4 due to CVE-2009-0037\n" );
838 if ( is_array( $this->postData ) ) {
842 if ( $this->parsedUrl[
'scheme'] !=
'http'
843 && $this->parsedUrl[
'scheme'] !=
'https' ) {
844 $this->status->fatal(
'http-invalid-scheme', $this->parsedUrl[
'scheme'] );
847 $this->reqHeaders[
'Accept'] =
"*/*";
848 $this->reqHeaders[
'Connection'] =
'Close';
849 if ( $this->method ==
'POST' ) {
851 $this->reqHeaders[
'Content-Length'] = strlen( $this->postData );
852 if ( !isset( $this->reqHeaders[
'Content-Type'] ) ) {
853 $this->reqHeaders[
'Content-Type'] =
"application/x-www-form-urlencoded";
860 'method' => $this->method,
862 'protocol_version' =>
'1.1',
863 'max_redirects' => $this->followRedirects ? $this->maxRedirects : 0,
864 'ignore_errors' =>
true,
865 'timeout' => $this->timeout,
867 'curl_verify_ssl_host' => $this->sslVerifyHost ? 2 : 0,
868 'curl_verify_ssl_peer' => $this->sslVerifyCert,
871 'verify_peer' => $this->sslVerifyCert,
872 'SNI_enabled' =>
true,
876 if ( $this->proxy ) {
877 $options[
'http'][
'proxy'] = $this->urlToTCP( $this->proxy );
878 $options[
'http'][
'request_fulluri'] =
true;
881 if ( $this->postData ) {
885 if ( $this->sslVerifyHost ) {
886 $options[
'ssl'][
'CN_match'] = $this->parsedUrl[
'host'];
889 if ( is_dir( $this->caInfo ) ) {
891 } elseif ( is_file( $this->caInfo ) ) {
893 } elseif ( $this->caInfo ) {
894 throw new MWException(
"Invalid CA info passed: {$this->caInfo}" );
897 $context = stream_context_create(
$options );
899 $this->headerList =
array();
908 $fh = fopen(
$url,
"r",
false, $context );
915 $result = stream_get_meta_data( $fh );
916 $this->headerList =
$result[
'wrapper_data'];
919 if ( !$this->followRedirects ) {
923 # Handle manual redirection
924 if ( !$this->
isRedirect() || $reqCount > $this->maxRedirects ) {
927 # Check security of URL
931 wfDebug( __METHOD__ .
": insecure redirection\n" );
938 if ( $fh ===
false ) {
939 $this->status->fatal(
'http-request-error' );
945 $this->status->fatal(
'http-timed-out', $this->url );
952 if ( $this->status->isOK() || (int)$this->respStatus >= 300 ) {
953 while ( !feof( $fh ) ) {
954 $buf = fread( $fh, 8192 );
956 if ( $buf ===
false ) {
957 $this->status->fatal(
'http-read-error' );
961 if ( strlen( $buf ) ) {
962 call_user_func( $this->callback, $fh, $buf );