diff --git a/CHANGELOG.md b/CHANGELOG.md index 37ea961..65fb53d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 This is an alpha version! The changes listed here are not final. +### Added +- Add rate limiter to the package versions endpoint calls. + ### Changed - Adjust 'get_site_id()' method to return null if there's no blog ID. - Updated package dependencies. diff --git a/src/class-package-version-tracker.php b/src/class-package-version-tracker.php index 697f672..c973bce 100644 --- a/src/class-package-version-tracker.php +++ b/src/class-package-version-tracker.php @@ -30,11 +30,26 @@ class Package_Version_Tracker { */ const CACHED_FAILED_REQUEST_EXPIRATION = 1 * HOUR_IN_SECONDS; + /** + * Transient key for rate limiting the package version requests; + */ + const RATE_LIMITER_KEY = 'jetpack_update_remote_package_last_query'; + + /** + * Only allow one versions check (and request) per minute. + */ + const RATE_LIMITER_TIMEOUT = MINUTE_IN_SECONDS; + /** * Uses the jetpack_package_versions filter to obtain the package versions from packages that need * version tracking. If the package versions have changed, updates the option and notifies WPCOM. */ public function maybe_update_package_versions() { + if ( $this->is_rate_limiting() ) { + // The version check is being rate limited. + return; + } + /** * Obtains the package versions. * @@ -108,4 +123,19 @@ protected function update_package_versions_option( $package_versions ) { set_transient( self::CACHED_FAILED_REQUEST_KEY, time(), self::CACHED_FAILED_REQUEST_EXPIRATION ); } } + + /** + * Check if version check is being rate limited, and update the rate limiting transient if needed. + * + * @return bool + */ + private function is_rate_limiting() { + if ( get_transient( static::RATE_LIMITER_KEY ) ) { + return true; + } + + set_transient( static::RATE_LIMITER_KEY, time(), static::RATE_LIMITER_TIMEOUT ); + + return false; + } } diff --git a/tests/php/test_package_version_tracker.php b/tests/php/test_package_version_tracker.php index d9e48cc..5903c69 100644 --- a/tests/php/test_package_version_tracker.php +++ b/tests/php/test_package_version_tracker.php @@ -57,6 +57,9 @@ public function tear_down() { $this->http_request_attempted = false; Constants::clear_constants(); WorDBless_Options::init()->clear_options(); + + delete_transient( Package_Version_Tracker::CACHED_FAILED_REQUEST_KEY ); + delete_transient( Package_Version_Tracker::RATE_LIMITER_KEY ); } /** @@ -317,9 +320,34 @@ function () { $failed_request_cached = get_transient( Package_Version_Tracker::CACHED_FAILED_REQUEST_KEY ); $this->assertNotFalse( $failed_request_cached ); + } - // Clean-up. - delete_transient( Package_Version_Tracker::CACHED_FAILED_REQUEST_KEY ); + /** + * Tests the maybe_update_package_versions method with rate limit applied. + */ + public function test_maybe_update_package_versions_with_rate_limit() { + \Jetpack_Options::update_option( 'blog_token', 'asdasd.123123' ); + \Jetpack_Options::update_option( 'id', 1234 ); + + add_filter( 'pre_http_request', array( $this, 'intercept_http_request_success' ) ); + + update_option( Package_Version_Tracker::PACKAGE_VERSION_OPTION, self::PACKAGE_VERSIONS ); + set_transient( Package_Version_Tracker::RATE_LIMITER_KEY, time() ); + + add_filter( + 'jetpack_package_versions', + function () { + return self::CHANGED_VERSIONS; + } + ); + + ( new Package_Version_Tracker() )->maybe_update_package_versions(); + + remove_filter( 'pre_http_request', array( $this, 'intercept_http_request_success' ) ); + + $this->assertFalse( $this->http_request_attempted ); + + $this->assertSame( self::PACKAGE_VERSIONS, get_option( Package_Version_Tracker::PACKAGE_VERSION_OPTION ) ); } /** @@ -349,9 +377,6 @@ function () { $this->assertFalse( $this->http_request_attempted ); $this->assertSame( self::PACKAGE_VERSIONS, get_option( Package_Version_Tracker::PACKAGE_VERSION_OPTION ) ); - - // Clean-up. - delete_transient( Package_Version_Tracker::CACHED_FAILED_REQUEST_KEY ); } /**