From e94d76d165ae335e6038cafbe33e79a2fed220c3 Mon Sep 17 00:00:00 2001 From: Sergey Mitroshin Date: Fri, 2 Feb 2024 02:10:44 -0500 Subject: [PATCH] Connection: Add rate limiter to the package versions endpoint calls (#35379) * Connection: Add rate limiter to the package versions endpoint calls. --------- Co-authored-by: Foteini Giannaropoulou --- .../add-package-versions-rate-limiter | 4 +++ .../src/class-package-version-tracker.php | 30 ++++++++++++++++ .../php/test_package_version_tracker.php | 35 ++++++++++++++++--- 3 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 projects/packages/connection/changelog/add-package-versions-rate-limiter diff --git a/projects/packages/connection/changelog/add-package-versions-rate-limiter b/projects/packages/connection/changelog/add-package-versions-rate-limiter new file mode 100644 index 0000000000000..d5d47c787fdc4 --- /dev/null +++ b/projects/packages/connection/changelog/add-package-versions-rate-limiter @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Add rate limiter to the package versions endpoint calls. diff --git a/projects/packages/connection/src/class-package-version-tracker.php b/projects/packages/connection/src/class-package-version-tracker.php index 697f67230eb8f..c973bce03dcb7 100644 --- a/projects/packages/connection/src/class-package-version-tracker.php +++ b/projects/packages/connection/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/projects/packages/connection/tests/php/test_package_version_tracker.php b/projects/packages/connection/tests/php/test_package_version_tracker.php index d9e48cc46be6a..5903c6993f592 100644 --- a/projects/packages/connection/tests/php/test_package_version_tracker.php +++ b/projects/packages/connection/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 ); } /**