23use Psr\Log\LoggerAwareInterface;
24use Psr\Log\LoggerInterface;
25use Psr\Log\NullLogger;
40 protected $timeout =
'default';
52 protected $reqHeaders = [];
70 protected $respHeaders = [];
100 $url, array $options = [], $caller = __METHOD__,
Profiler $profiler =
null
105 $this->logger = $options[
'logger'] ??
new NullLogger();
107 if ( !$this->parsedUrl || !self::isValidURI( $this->url ) ) {
108 $this->status = StatusValue::newFatal(
'http-invalid-url',
$url );
110 $this->status = StatusValue::newGood( 100 );
113 if ( isset( $options[
'timeout'] ) && $options[
'timeout'] !=
'default' ) {
114 $this->timeout = $options[
'timeout'];
118 wfDeprecated( __METHOD__ .
' without the timeout option',
'1.35' );
119 $httpTimeout = MediaWikiServices::getInstance()->getMainConfig()->get(
120 MainConfigNames::HTTPTimeout );
121 $this->timeout = $httpTimeout;
123 if ( isset( $options[
'connectTimeout'] ) && $options[
'connectTimeout'] !=
'default' ) {
124 $this->connectTimeout = $options[
'connectTimeout'];
128 wfDeprecated( __METHOD__ .
' without the connectTimeout option',
'1.35' );
129 $httpConnectTimeout = MediaWikiServices::getInstance()->getMainConfig()->get(
130 MainConfigNames::HTTPConnectTimeout );
131 $this->connectTimeout = $httpConnectTimeout;
133 if ( isset( $options[
'userAgent'] ) ) {
136 if ( isset( $options[
'username'] ) && isset( $options[
'password'] ) ) {
139 'Basic ' . base64_encode( $options[
'username'] .
':' . $options[
'password'] )
142 if ( isset( $options[
'originalRequest'] ) ) {
146 $this->
setHeader(
'X-Request-Id', WebRequest::getRequestId() );
148 $members = [
"postData",
"proxy",
"noProxy",
"sslVerifyHost",
"caInfo",
149 "method",
"followRedirects",
"maxRedirects",
"sslVerifyCert",
"callback" ];
151 foreach ( $members as $o ) {
152 if ( isset( $options[$o] ) ) {
155 if ( $o ==
'method' ) {
157 $options[$o] = strtoupper( $options[$o] );
159 $this->$o = $options[$o];
163 if ( $this->noProxy ) {
168 $this->profiler = $profiler;
169 $this->profileName = $caller;
176 $this->logger = $logger;
185 return function_exists(
'curl_init' ) ||
wfIniGetBool(
'allow_url_fopen' );
198 public static function factory( $url, array $options =
null, $caller = __METHOD__ ) {
199 if ( $options ===
null ) {
202 return MediaWikiServices::getInstance()->getHttpRequestFactory()
203 ->create(
$url, $options, $caller );
222 $this->postData =
$args;
231 $httpProxy = MediaWikiServices::getInstance()->getMainConfig()->get(
232 MainConfigNames::HTTPProxy );
233 $localHTTPProxy = MediaWikiServices::getInstance()->getMainConfig()->get(
234 MainConfigNames::LocalHTTPProxy );
236 if ( $this->noProxy ) {
242 if ( $this->proxy ) {
248 if ( self::isLocalURL( $this->url ) ) {
249 if ( $localHTTPProxy !==
false ) {
253 $this->proxy = (string)$httpProxy;
269 if ( $parsedProxy ===
false ) {
270 throw new Exception(
"Invalid reverseProxy configured: $proxy" );
273 $this->
setHeader(
'Host', $this->parsedUrl[
'host'] );
275 $this->parsedUrl[
'scheme'] = $parsedProxy[
'scheme'];
276 $this->parsedUrl[
'host'] = $parsedProxy[
'host'];
277 if ( isset( $parsedProxy[
'port'] ) ) {
278 $this->parsedUrl[
'port'] = $parsedProxy[
'port'];
280 unset( $this->parsedUrl[
'port'] );
284 $this->noProxy =
true;
293 private static function isLocalURL( $url ) {
294 $commandLineMode = MediaWikiServices::getInstance()->getMainConfig()->get(
'CommandLineMode' );
295 $localVirtualHosts = MediaWikiServices::getInstance()->getMainConfig()->get(
296 MainConfigNames::LocalVirtualHosts );
297 if ( $commandLineMode ) {
303 if ( preg_match(
'!^https?://([\w.-]+)[/:].*$!', $url,
$matches ) ) {
306 $domainParts = explode(
'.', $host );
308 $domainParts = array_reverse( $domainParts );
311 $countParts = count( $domainParts );
312 for ( $i = 0; $i < $countParts; $i++ ) {
313 $domainPart = $domainParts[$i];
315 $domain = $domainPart;
317 $domain = $domainPart .
'.' . $domain;
320 if ( in_array( $domain, $localVirtualHosts ) ) {
343 $this->reqHeaders[$name] = $value;
353 if ( $this->cookieJar ) {
354 $this->reqHeaders[
'Cookie'] =
355 $this->cookieJar->serializeToHttpRequest(
356 $this->parsedUrl[
'path'],
357 $this->parsedUrl[
'host']
361 foreach ( $this->reqHeaders as $name => $value ) {
362 $list[] =
"$name: $value";
398 if ( $callback ===
null ) {
399 $callback = [ $this,
'read' ];
400 } elseif ( !is_callable( $callback ) ) {
401 $this->status->fatal(
'http-internal-error' );
402 throw new InvalidArgumentException( __METHOD__ .
': invalid callback' );
404 $this->callback = $callback;
428 throw new LogicException(
'children must override this' );
434 if ( strtoupper( $this->method ) ==
"HEAD" ) {
435 $this->headersOnly =
true;
440 if ( !$this->callback ) {
444 if ( !isset( $this->reqHeaders[
'User-Agent'] ) ) {
445 $http = MediaWikiServices::getInstance()->getHttpRequestFactory();
459 if ( !$this->status->isOK() ) {
460 $this->respStatus =
'0 Error';
463 foreach ( $this->headerList as
$header ) {
464 if ( preg_match(
"#^HTTP/([0-9.]+) (.*)#",
$header, $match ) ) {
465 $this->respVersion = $match[1];
466 $this->respStatus = $match[2];
467 } elseif ( preg_match(
"#^[ \t]#",
$header ) ) {
468 $last = count( $this->respHeaders[$lastname] ) - 1;
469 $this->respHeaders[$lastname][$last] .=
"\r\n$header";
470 } elseif ( preg_match(
"#^([^:]*):[\t ]*(.*)#",
$header, $match ) ) {
471 $this->respHeaders[strtolower( $match[1] )][] = $match[2];
472 $lastname = strtolower( $match[1] );
487 if ( !$this->respHeaders ) {
491 if ( (
int)$this->respStatus > 0 && (
int)$this->respStatus < 400 ) {
492 $this->status->setResult(
true, (
int)$this->respStatus );
494 list( $code, $message ) = explode(
" ", $this->respStatus, 2 );
495 $this->status->setResult(
false, (
int)$this->respStatus );
496 $this->status->fatal(
"http-bad-status", $code, $message );
508 if ( !$this->respHeaders ) {
512 return (
int)$this->respStatus;
521 if ( !$this->respHeaders ) {
525 $status = (int)$this->respStatus;
527 if ( $status >= 300 && $status <= 303 ) {
544 if ( !$this->respHeaders ) {
548 return $this->respHeaders;
558 if ( !$this->respHeaders ) {
562 if ( isset( $this->respHeaders[strtolower(
$header )] ) ) {
563 $v = $this->respHeaders[strtolower(
$header )];
564 return $v[count( $v ) - 1];
578 $this->cookieJar = $jar;
587 if ( !$this->respHeaders ) {
591 return $this->cookieJar;
603 public function setCookie( $name, $value, array $attr = [] ) {
604 if ( !$this->cookieJar ) {
608 if ( $this->parsedUrl && !isset( $attr[
'domain'] ) ) {
609 $attr[
'domain'] = $this->parsedUrl[
'host'];
612 $this->cookieJar->
setCookie( $name, $value, $attr );
619 if ( !$this->cookieJar ) {
623 if ( isset( $this->respHeaders[
'set-cookie'] ) ) {
625 if ( !isset(
$url[
'host'] ) ) {
626 $this->status->fatal(
'http-invalid-url',
$url );
628 foreach ( $this->respHeaders[
'set-cookie'] as $cookie ) {
629 $this->cookieJar->parseCookieResponseHeader( $cookie,
$url[
'host'] );
655 if ( isset( $headers[
'location'] ) ) {
656 $locations = $headers[
'location'];
658 $foundRelativeURI =
false;
659 $countLocations = count( $locations );
661 for ( $i = $countLocations - 1; $i >= 0; $i-- ) {
662 $url = parse_url( $locations[$i] );
664 if ( isset(
$url[
'scheme'] ) && isset(
$url[
'host'] ) ) {
665 $domain =
$url[
'scheme'] .
'://' .
$url[
'host'];
668 $foundRelativeURI =
true;
672 if ( !$foundRelativeURI ) {
673 return $locations[$countLocations - 1];
676 return $domain . $locations[$countLocations - 1];
678 $url = parse_url( $this->url );
679 if ( isset(
$url[
'scheme'] ) && isset(
$url[
'host'] ) ) {
680 return $url[
'scheme'] .
'://' .
$url[
'host'] .
681 $locations[$countLocations - 1];
710 if ( $originalRequest instanceof
WebRequest ) {
712 'ip' => $originalRequest->getIP(),
713 'userAgent' => $originalRequest->getHeader(
'User-Agent' ),
716 !is_array( $originalRequest )
717 || array_diff( [
'ip',
'userAgent' ], array_keys( $originalRequest ) )
719 throw new InvalidArgumentException( __METHOD__ .
': $originalRequest must be a '
720 .
"WebRequest or an array with 'ip' and 'userAgent' keys" );
723 $this->reqHeaders[
'X-Forwarded-For'] = $originalRequest[
'ip'];
724 $this->reqHeaders[
'X-Original-User-Agent'] = $originalRequest[
'userAgent'];
744 return (
bool)preg_match(
745 '/^https?:\/\/[^\/\s]\S*$/D',
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
wfIniGetBool( $setting)
Safety wrapper around ini_get() for boolean settings.
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
wfAssembleUrl( $urlParts)
This function will reassemble a URL parsed with wfParseURL.
Cookie jar to use with MWHttpRequest.
setCookie( $name, $value, $attr)
Set a cookie in the cookie jar.
This wrapper class will call out to curl (if available) or fallback to regular PHP if necessary for h...
getContent()
Get the body, or content, of the response to the request.
setLogger(LoggerInterface $logger)
setCookie( $name, $value, array $attr=[])
Sets a cookie.
getResponseHeaders()
Returns an associative array of response headers after the request has been executed.
doSetCallback( $callback)
Worker function for setting callbacks.
setHeader( $name, $value)
Set an arbitrary header.
getCookieJar()
Returns the cookie jar in use.
setReverseProxy(string $proxy)
Enable use of a reverse proxy in which the hostname is passed as a "Host" header, and the request is ...
setOriginalRequest( $originalRequest)
Set information about the original request.
isRedirect()
Returns true if the last status code was a redirect.
read( $fh, $content)
A generic callback to read the body of the response from a remote server.
getFinalUrl()
Returns the final URL after all redirections.
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.
canFollowRedirects()
Returns true if the backend can follow redirects.
__construct( $url, array $options=[], $caller=__METHOD__, Profiler $profiler=null)
setCallback( $callback)
Set a read callback to accept data read from the HTTP request.
static canMakeRequests()
Simple function to test if we can make any sort of requests at all, using cURL or fopen()
static factory( $url, array $options=null, $caller=__METHOD__)
Generate a new request object.
static isValidURI( $uri)
Check that the given URI is a valid one.
getStatus()
Get the integer value of the HTTP status code (e.g.
const SUPPORTS_FILE_POSTS
execute()
Take care of whatever is necessary to perform the URI request.
getResponseHeader( $header)
Returns the value of the given response header.
proxySetup()
Take care of setting up the proxy (do nothing if "noProxy" is set)
setData(array $args)
Set the parameters of the request.
parseCookies()
Parse the cookies in the response headers and store them in the cookie jar.
getHeaderList()
Get an array of the headers.
setCookieJar(CookieJar $jar)
Tells the MWHttpRequest object to use this pre-loaded CookieJar.
A class containing constants representing the names of configuration variables.
Profiler base class that defines the interface and some shared functionality.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...