Add caching functionality for IP lookup

Instead of always looking up a single IP address, even for multiple
requests, cache the result of the first lookup. IP addresses usually
doesn't move that fast, caching the result should therefore be
reasonably ok.

Change-Id: Ice78ec08ff886e77c542a75086610498eaa6c6b4
This commit is contained in:
Florian Schmidt 2018-07-18 12:37:17 +02:00
parent fed15fd7a5
commit 6f519c9618
3 changed files with 74 additions and 15 deletions

View file

@ -3,14 +3,19 @@
class CookieWarningDecisions {
private $config;
private $geoLocation;
private $cache;
const CACHE_KEY = 'cookieWarningIpLookupCache:';
/**
* @param Config $config
* @param GeoLocation $geoLocation
* @param WANObjectCache $cache
*/
public function __construct( Config $config, GeoLocation $geoLocation ) {
public function __construct( Config $config, GeoLocation $geoLocation, WANObjectCache $cache ) {
$this->config = $config;
$this->geoLocation = $geoLocation;
$this->cache = $cache;
}
/**
@ -35,8 +40,6 @@ class CookieWarningDecisions {
/**
* Checks, if the user is in one of the configured regions.
*
* @TODO: This function or the function users should set the cookie or user option, if this
* function returns false to avoid a location lookup on each request.
* @param IContextSource $context
* @return bool
* @throws ConfigException
@ -50,18 +53,9 @@ class CookieWarningDecisions {
return true;
}
wfDebugLog( 'CookieWarning', 'Try to locate the user\'s IP address.' );
$located = $this->geoLocation->locate( $context->getRequest()->getIP() );
if ( !$located ) {
wfDebugLog( 'CookieWarning',
'Locating the user\'s IP address failed or is' . ' configured false.' );
$countryCode = $this->getCountryCodeFromIP( $context->getRequest()->getIP() );
return true;
}
wfDebugLog( 'CookieWarning', 'Locating the user was successful, located' . ' region: ' .
$this->geoLocation->getCountryCode() );
return array_key_exists( $this->geoLocation->getCountryCode(),
return array_key_exists( $countryCode,
$this->config->get( 'CookieWarningForCountryCodes' ) );
}
@ -73,4 +67,35 @@ class CookieWarningDecisions {
return $this->config->get( 'CookieWarningGeoIPLookup' ) === 'js' &&
is_array( $this->config->get( 'CookieWarningForCountryCodes' ) );
}
/**
* @param $currentIP
* @return bool|mixed|null|string
* @throws ConfigException
*/
private function getCountryCodeFromIP( $currentIP ) {
$cacheKey = $this->cache->makeGlobalKey( __CLASS__, self::CACHE_KEY . $currentIP );
$lookedUpCountryCode = $this->cache->get( $cacheKey );
if ( is_string( $lookedUpCountryCode ) ) {
return $lookedUpCountryCode;
}
wfDebugLog( 'CookieWarning', 'Try to locate the user\'s IP address.' );
$located = $this->geoLocation->locate( $currentIP );
if ( !$located ) {
wfDebugLog( 'CookieWarning',
'Locating the user\'s IP address failed or is' . ' configured false.' );
return true;
}
$lookedUpCountryCode = $this->geoLocation->getCountryCode();
$this->cache->set( $cacheKey, $lookedUpCountryCode );
wfDebugLog( 'CookieWarning', 'Locating the user was successful, located' . ' region: ' .
$lookedUpCountryCode );
return $lookedUpCountryCode;
}
}

View file

@ -12,6 +12,6 @@ return [
},
'CookieWarning.Decisions' => function ( MediaWikiServices $services ) {
return new CookieWarningDecisions( $services->getService( 'CookieWarning.Config' ),
$services->getService( 'GeoLocation' ) );
$services->getService( 'GeoLocation' ), $services->getMainWANObjectCache() );
},
];

View file

@ -0,0 +1,34 @@
<?php
use MediaWiki\MediaWikiServices;
class CookieWarningDecisionsTest extends MediaWikiTestCase {
/**
* @covers CookieWarningDecisions::shouldShowCookieWarning()
* @throws ConfigException
* @throws MWException
*/
public function testShouldNotCallGeoLocationMultiple() {
$this->setMwGlobals( [
'wgCookieWarningEnabled' => true,
'wgCookieWarningGeoIPLookup' => 'php',
'wgCookieWarningForCountryCodes' => [ 'EU' => 'European Union' ],
] );
$geoLocation = $this->getMockBuilder( GeoLocation::class )
->disableOriginalConstructor()
->getMock();
$geoLocation->method( 'locate' )->willReturn( true );
$geoLocation->method( 'getCountryCode' )->willReturn( 'EU' );
$geoLocation->expects( $this->once() )->method( 'locate' );
$cookieWarningDecisions = new CookieWarningDecisions(
MediaWikiServices::getInstance()->getService( 'CookieWarning.Config' ),
$geoLocation,
new WANObjectCache( [ 'cache' => new HashBagOStuff() ] )
);
$cookieWarningDecisions->shouldShowCookieWarning( RequestContext::getMain() );
$cookieWarningDecisions->shouldShowCookieWarning( RequestContext::getMain() );
}
}