21 use Psr\Log\LoggerInterface;
22 use Psr\Log\LoggerAwareInterface;
23 use Psr\Log\NullLogger;
106 $this->logger = $options[
'logger'] ??
new NullLogger();
114 if ( isset( $options[
'timeout'] ) && $options[
'timeout'] !=
'default' ) {
115 $this->timeout = $options[
'timeout'];
119 if ( isset( $options[
'connectTimeout'] ) && $options[
'connectTimeout'] !=
'default' ) {
120 $this->connectTimeout = $options[
'connectTimeout'];
124 if ( isset( $options[
'userAgent'] ) ) {
127 if ( isset( $options[
'username'] ) && isset( $options[
'password'] ) ) {
130 'Basic ' . base64_encode( $options[
'username'] .
':' . $options[
'password'] )
133 if ( isset( $options[
'originalRequest'] ) ) {
139 $members = [
"postData",
"proxy",
"noProxy",
"sslVerifyHost",
"caInfo",
140 "method",
"followRedirects",
"maxRedirects",
"sslVerifyCert",
"callback" ];
142 foreach ( $members as $o ) {
143 if ( isset( $options[$o] ) ) {
146 if ( $o ==
'method' ) {
148 $options[$o] = strtoupper( $options[$o] );
150 $this->$o = $options[$o];
154 if ( $this->noProxy ) {
160 $this->profileName = $caller;
176 return function_exists(
'curl_init' ) ||
wfIniGetBool(
'allow_url_fopen' );
189 public static function factory(
$url, array $options =
null, $caller = __METHOD__ ) {
190 if ( $options ===
null ) {
193 return \MediaWiki\MediaWikiServices::getInstance()
194 ->getHttpRequestFactory()
195 ->create(
$url, $options, $caller );
214 $this->postData =
$args;
224 if ( $this->proxy && !$this->noProxy ) {
230 if ( self::isLocalURL( $this->url ) || $this->noProxy ) {
253 if ( preg_match(
'!^https?://([\w.-]+)[/:].*$!',
$url,
$matches ) ) {
256 $domainParts = explode(
'.', $host );
258 $domainParts = array_reverse( $domainParts );
261 $countParts = count( $domainParts );
262 for ( $i = 0; $i < $countParts; $i++ ) {
263 $domainPart = $domainParts[$i];
265 $domain = $domainPart;
267 $domain = $domainPart .
'.' . $domain;
294 $this->reqHeaders[$name] = $value;
304 if ( $this->cookieJar ) {
305 $this->reqHeaders[
'Cookie'] =
306 $this->cookieJar->serializeToHttpRequest(
307 $this->parsedUrl[
'path'],
308 $this->parsedUrl[
'host']
312 foreach ( $this->reqHeaders as $name => $value ) {
313 $list[] =
"$name: $value";
352 $this->status->fatal(
'http-internal-error' );
353 throw new InvalidArgumentException( __METHOD__ .
': invalid callback' );
379 throw new LogicException(
'children must override this' );
385 if ( strtoupper( $this->method ) ==
"HEAD" ) {
386 $this->headersOnly =
true;
391 if ( !$this->callback ) {
395 if ( !isset( $this->reqHeaders[
'User-Agent'] ) ) {
409 if ( !$this->status->isOK() ) {
410 $this->respStatus =
'0 Error';
413 foreach ( $this->headerList as
$header ) {
414 if ( preg_match(
"#^HTTP/([0-9.]+) (.*)#",
$header, $match ) ) {
415 $this->respVersion = $match[1];
416 $this->respStatus = $match[2];
417 } elseif ( preg_match(
"#^[ \t]#",
$header ) ) {
418 $last = count( $this->respHeaders[$lastname] ) - 1;
419 $this->respHeaders[$lastname][
$last] .=
"\r\n$header";
420 } elseif ( preg_match(
"#^([^:]*):[\t ]*(.*)#",
$header, $match ) ) {
421 $this->respHeaders[strtolower( $match[1] )][] = $match[2];
422 $lastname = strtolower( $match[1] );
437 if ( !$this->respHeaders ) {
441 if ( ( (
int)$this->respStatus > 0 && (
int)$this->respStatus < 400 ) ) {
442 $this->status->setResult(
true, (
int)$this->respStatus );
444 list( $code, $message ) = explode(
" ", $this->respStatus, 2 );
445 $this->status->setResult(
false, (
int)$this->respStatus );
446 $this->status->fatal(
"http-bad-status", $code, $message );
458 if ( !$this->respHeaders ) {
471 if ( !$this->respHeaders ) {
475 $status = (int)$this->respStatus;
494 if ( !$this->respHeaders ) {
508 if ( !$this->respHeaders ) {
512 if ( isset( $this->respHeaders[strtolower(
$header )] ) ) {
513 $v = $this->respHeaders[strtolower(
$header )];
514 return $v[count( $v ) - 1];
528 $this->cookieJar = $jar;
537 if ( !$this->respHeaders ) {
553 public function setCookie( $name, $value, array $attr = [] ) {
554 if ( !$this->cookieJar ) {
558 if ( $this->parsedUrl && !isset( $attr[
'domain'] ) ) {
559 $attr[
'domain'] = $this->parsedUrl[
'host'];
562 $this->cookieJar->
setCookie( $name, $value, $attr );
569 if ( !$this->cookieJar ) {
573 if ( isset( $this->respHeaders[
'set-cookie'] ) ) {
575 foreach ( $this->respHeaders[
'set-cookie'] as $cookie ) {
576 $this->cookieJar->parseCookieResponseHeader( $cookie,
$url[
'host'] );
601 if ( isset( $headers[
'location'] ) ) {
602 $locations = $headers[
'location'];
604 $foundRelativeURI =
false;
605 $countLocations = count( $locations );
607 for ( $i = $countLocations - 1; $i >= 0; $i-- ) {
608 $url = parse_url( $locations[$i] );
610 if ( isset(
$url[
'host'] ) ) {
611 $domain =
$url[
'scheme'] .
'://' .
$url[
'host'];
614 $foundRelativeURI =
true;
618 if ( !$foundRelativeURI ) {
619 return $locations[$countLocations - 1];
622 return $domain . $locations[$countLocations - 1];
624 $url = parse_url( $this->url );
625 if ( isset(
$url[
'host'] ) ) {
626 return $url[
'scheme'] .
'://' .
$url[
'host'] .
627 $locations[$countLocations - 1];
656 if ( $originalRequest instanceof
WebRequest ) {
658 'ip' => $originalRequest->getIP(),
659 'userAgent' => $originalRequest->getHeader(
'User-Agent' ),
662 !is_array( $originalRequest )
663 || array_diff( [
'ip',
'userAgent' ], array_keys( $originalRequest ) )
665 throw new InvalidArgumentException( __METHOD__ .
': $originalRequest must be a '
666 .
"WebRequest or an array with 'ip' and 'userAgent' keys" );
669 $this->reqHeaders[
'X-Forwarded-For'] = $originalRequest[
'ip'];
670 $this->reqHeaders[
'X-Original-User-Agent'] = $originalRequest[
'userAgent'];
690 return (
bool)preg_match(
691 '/^https?:\/\/[^\/\s]\S*$/D',