23 use Psr\Log\LoggerAwareInterface;
24 use Psr\Log\LoggerInterface;
25 use Psr\Log\NullLogger;
105 $this->logger = $options[
'logger'] ??
new NullLogger();
107 if ( !$this->parsedUrl || !self::isValidURI( $this->url ) ) {
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'] ) ) {
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 ) {
169 $this->profileName = $caller;
185 return function_exists(
'curl_init' ) ||
wfIniGetBool(
'allow_url_fopen' );
198 public static function factory(
$url, array $options =
null, $caller = __METHOD__ ) {
200 return MediaWikiServices::getInstance()->getHttpRequestFactory()
201 ->create(
$url, $options ?? [], $caller );
220 $this->postData = $args;
229 $httpProxy = MediaWikiServices::getInstance()->getMainConfig()->get(
230 MainConfigNames::HTTPProxy );
231 $localHTTPProxy = MediaWikiServices::getInstance()->getMainConfig()->get(
232 MainConfigNames::LocalHTTPProxy );
234 if ( $this->noProxy ) {
240 if ( $this->proxy ) {
246 if ( self::isLocalURL( $this->url ) ) {
247 if ( $localHTTPProxy !==
false ) {
251 $this->proxy = (string)$httpProxy;
267 if ( $parsedProxy ===
false ) {
268 throw new Exception(
"Invalid reverseProxy configured: $proxy" );
271 $this->
setHeader(
'Host', $this->parsedUrl[
'host'] );
273 $this->parsedUrl[
'scheme'] = $parsedProxy[
'scheme'];
274 $this->parsedUrl[
'host'] = $parsedProxy[
'host'];
275 if ( isset( $parsedProxy[
'port'] ) ) {
276 $this->parsedUrl[
'port'] = $parsedProxy[
'port'];
278 unset( $this->parsedUrl[
'port'] );
282 $this->noProxy =
true;
291 private static function isLocalURL(
$url ) {
292 $commandLineMode = MediaWikiServices::getInstance()->getMainConfig()->get(
'CommandLineMode' );
293 $localVirtualHosts = MediaWikiServices::getInstance()->getMainConfig()->get(
294 MainConfigNames::LocalVirtualHosts );
295 if ( $commandLineMode ) {
301 if ( preg_match(
'!^https?://([\w.-]+)[/:].*$!',
$url,
$matches ) ) {
304 $domainParts = explode(
'.', $host );
306 $domainParts = array_reverse( $domainParts );
309 $countParts = count( $domainParts );
310 for ( $i = 0; $i < $countParts; $i++ ) {
311 $domainPart = $domainParts[$i];
313 $domain = $domainPart;
315 $domain = $domainPart .
'.' . $domain;
318 if ( in_array( $domain, $localVirtualHosts ) ) {
341 $this->reqHeaders[$name] = $value;
351 if ( $this->cookieJar ) {
352 $this->reqHeaders[
'Cookie'] =
353 $this->cookieJar->serializeToHttpRequest(
354 $this->parsedUrl[
'path'],
355 $this->parsedUrl[
'host']
359 foreach ( $this->reqHeaders as $name => $value ) {
360 $list[] =
"$name: $value";
399 $this->status->fatal(
'http-internal-error' );
400 throw new InvalidArgumentException( __METHOD__ .
': invalid callback' );
426 throw new LogicException(
'children must override this' );
432 if ( strtoupper( $this->method ) ==
"HEAD" ) {
433 $this->headersOnly =
true;
438 if ( !$this->callback ) {
442 if ( !isset( $this->reqHeaders[
'User-Agent'] ) ) {
443 $http = MediaWikiServices::getInstance()->getHttpRequestFactory();
457 if ( !$this->status->isOK() ) {
458 $this->respStatus =
'0 Error';
461 foreach ( $this->headerList as
$header ) {
462 if ( preg_match(
"#^HTTP/([0-9.]+) (.*)#",
$header, $match ) ) {
463 $this->respVersion = $match[1];
464 $this->respStatus = $match[2];
465 } elseif ( preg_match(
"#^[ \t]#",
$header ) ) {
466 $last = count( $this->respHeaders[$lastname] ) - 1;
467 $this->respHeaders[$lastname][$last] .=
"\r\n$header";
468 } elseif ( preg_match(
"#^([^:]*):[\t ]*(.*)#",
$header, $match ) ) {
469 $this->respHeaders[strtolower( $match[1] )][] = $match[2];
470 $lastname = strtolower( $match[1] );
485 if ( !$this->respHeaders ) {
489 if ( (
int)$this->respStatus > 0 && (
int)$this->respStatus < 400 ) {
490 $this->status->setResult(
true, (
int)$this->respStatus );
492 [ $code, $message ] = explode(
" ", $this->respStatus, 2 );
493 $this->status->setResult(
false, (
int)$this->respStatus );
494 $this->status->fatal(
"http-bad-status", $code, $message );
506 if ( !$this->respHeaders ) {
519 if ( !$this->respHeaders ) {
523 $status = (int)$this->respStatus;
542 if ( !$this->respHeaders ) {
556 if ( !$this->respHeaders ) {
560 if ( isset( $this->respHeaders[strtolower(
$header )] ) ) {
561 $v = $this->respHeaders[strtolower(
$header )];
562 return $v[count( $v ) - 1];
576 $this->cookieJar = $jar;
585 if ( !$this->respHeaders ) {
601 public function setCookie( $name, $value, array $attr = [] ) {
602 if ( !$this->cookieJar ) {
606 if ( $this->parsedUrl && !isset( $attr[
'domain'] ) ) {
607 $attr[
'domain'] = $this->parsedUrl[
'host'];
610 $this->cookieJar->
setCookie( $name, $value, $attr );
617 if ( !$this->cookieJar ) {
621 if ( isset( $this->respHeaders[
'set-cookie'] ) ) {
623 if ( !isset(
$url[
'host'] ) ) {
624 $this->status->fatal(
'http-invalid-url',
$url );
626 foreach ( $this->respHeaders[
'set-cookie'] as $cookie ) {
627 $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[
'scheme'] ) && isset(
$url[
'host'] ) ) {
663 $domain =
$url[
'scheme'] .
'://' .
$url[
'host'];
666 $foundRelativeURI =
true;
670 if ( !$foundRelativeURI ) {
671 return $locations[$countLocations - 1];
674 return $domain . $locations[$countLocations - 1];
676 $url = parse_url( $this->url );
677 if ( isset(
$url[
'scheme'] ) && isset(
$url[
'host'] ) ) {
678 return $url[
'scheme'] .
'://' .
$url[
'host'] .
679 $locations[$countLocations - 1];
708 if ( $originalRequest instanceof
WebRequest ) {
710 'ip' => $originalRequest->getIP(),
711 'userAgent' => $originalRequest->getHeader(
'User-Agent' ),
714 !is_array( $originalRequest )
715 || array_diff( [
'ip',
'userAgent' ], array_keys( $originalRequest ) )
717 throw new InvalidArgumentException( __METHOD__ .
': $originalRequest must be a '
718 .
"WebRequest or an array with 'ip' and 'userAgent' keys" );
721 $this->reqHeaders[
'X-Forwarded-For'] = $originalRequest[
'ip'];
722 $this->reqHeaders[
'X-Original-User-Agent'] = $originalRequest[
'userAgent'];
742 return (
bool)preg_match(
743 '/^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.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
static newGood( $value=null)
Factory function for good results.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
static getRequestId()
Get the current request ID.