-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Improve robustness of page cache test for Site Health #10855
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
d4817d6
a2d3183
9aa2b94
ed21256
a6ffaaf
f9512d5
5a7b2f5
de5b813
c51daae
913d8cb
c63928a
6f1ff6a
4af0c36
1aded16
c67ffac
40770d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3428,22 +3428,20 @@ public function is_development_environment() { | |
| } | ||
|
|
||
| /** | ||
| * Returns a list of headers and its verification callback to verify if page cache is enabled or not. | ||
| * | ||
| * Note: key is header name and value could be callable function to verify header value. | ||
| * Empty value mean existence of header detect page cache is enabled. | ||
| * Returns a mapping from response headers to an optional callback to verify if page cache is enabled or not. | ||
| * | ||
| * @since 6.1.0 | ||
| * | ||
| * @return array List of client caching headers and their (optional) verification callbacks. | ||
| * @return array<string, ?callable> Mapping of page caching headers and their (optional) verification callbacks. | ||
| */ | ||
| public function get_page_cache_headers() { | ||
| public function get_page_cache_headers(): array { | ||
|
|
||
| $cache_hit_callback = static function ( $header_value ) { | ||
| return str_contains( strtolower( $header_value ), 'hit' ); | ||
| return 1 === preg_match( '/(^| |,)HIT(,| |$)/i', $header_value ); | ||
| }; | ||
|
|
||
| $cache_headers = array( | ||
| // Standard HTTP caching headers. | ||
| 'cache-control' => static function ( $header_value ) { | ||
| return (bool) preg_match( '/max-age=[1-9]/', $header_value ); | ||
| }, | ||
|
|
@@ -3453,36 +3451,97 @@ public function get_page_cache_headers() { | |
| 'age' => static function ( $header_value ) { | ||
| return is_numeric( $header_value ) && $header_value > 0; | ||
| }, | ||
| 'last-modified' => '', | ||
| 'etag' => '', | ||
| 'last-modified' => null, | ||
| 'etag' => null, | ||
| 'via' => null, | ||
|
|
||
| /** | ||
| * Custom caching headers. | ||
| * | ||
| * These do not seem to be actually used by any caching layers. There were first introduced in a Site Health | ||
| * test in the AMP plugin. They were copied into the Performance Lab plugin's Site Health test before they | ||
| * were merged into core. | ||
| * | ||
| * @link https://github.com/ampproject/amp-wp/pull/6849 | ||
| * @link https://github.com/WordPress/performance/pull/263 | ||
| * @link https://core.trac.wordpress.org/changeset/54043 | ||
| */ | ||
| 'x-cache-enabled' => static function ( $header_value ) { | ||
| return 'true' === strtolower( $header_value ); | ||
| return ( 'true' === strtolower( $header_value ) ); | ||
| }, | ||
| 'x-cache-disabled' => static function ( $header_value ) { | ||
| return ( 'on' !== strtolower( $header_value ) ); | ||
| }, | ||
|
Comment on lines
+3458
to
3474
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm inclined to remove these entirely since it seems they were originally introduced erroneously in the AMP plugin. I can't find any caching layers that actually use them. There are a few hundred mentions of them on GitHub, nevertheless: https://github.com/search?q=%2Fx-cache-(enabled%7Cdisabled)%2F&type=code |
||
| 'x-srcache-store-status' => $cache_hit_callback, | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was incorrect. Now fixed. |
||
| 'x-srcache-fetch-status' => $cache_hit_callback, | ||
|
|
||
| // Generic caching proxies (Nginx, Varnish, etc.) | ||
| /** | ||
| * CloudFlare. | ||
| * | ||
| * @link https://developers.cloudflare.com/cache/concepts/cache-responses/ | ||
| */ | ||
| 'cf-cache-status' => $cache_hit_callback, | ||
|
|
||
| /** | ||
| * Fastly. | ||
| * | ||
| * @link https://www.fastly.com/documentation/reference/http/http-headers/X-Cache/ | ||
| */ | ||
| 'x-cache' => $cache_hit_callback, | ||
| 'x-cache-status' => $cache_hit_callback, | ||
|
|
||
| /** | ||
| * LightSpeed. | ||
| * | ||
| * @link https://docs.litespeedtech.com/lscache/devguide/controls/#x-litespeed-cache | ||
| */ | ||
| 'x-litespeed-cache' => $cache_hit_callback, | ||
|
|
||
| /** | ||
| * OpenResty srcache-nginx-module. | ||
| * | ||
| * The `x-srcache-store-status` header indicates if the response was stored in the cache. | ||
| * Valid values include `STORE` and `BYPASS`. | ||
| * | ||
| * The `x-srcache-fetch-status` header indicates if the response was fetched from the cache. | ||
| * Valid values include `HIT`, `MISS`, and `BYPASS`. | ||
| * | ||
| * @link https://github.com/openresty/srcache-nginx-module | ||
| */ | ||
| 'x-srcache-store-status' => static function ( $header_value ) { | ||
| return 'store' === strtolower( $header_value ); | ||
| }, | ||
| 'x-srcache-fetch-status' => $cache_hit_callback, | ||
|
|
||
| /** | ||
| * Nginx. | ||
| * | ||
| * @link https://blog.nginx.org/blog/nginx-caching-guide | ||
| * @link https://www.inmotionhosting.com/support/website/nginx-cache-management/ | ||
| */ | ||
| 'x-cache-status' => $cache_hit_callback, | ||
| 'x-proxy-cache' => $cache_hit_callback, | ||
| 'via' => '', | ||
|
|
||
| // Cloudflare | ||
| 'cf-cache-status' => $cache_hit_callback, | ||
| /** | ||
| * Varnish Cache. | ||
| * | ||
| * A header with a single number indicates it was not cached. If there are two numbers (or more), then this | ||
| * indicates the response was cached. | ||
| * | ||
| * @link https://vinyl-cache.org/docs/2.1/faq/http.html | ||
| * @link https://www.fastly.com/documentation/reference/http/http-headers/X-Varnish/ | ||
| * @link https://www.linuxjournal.com/content/speed-your-web-site-varnish | ||
| */ | ||
| 'x-varnish' => static function ( $header_value ) { | ||
| return 1 === preg_match( '/^\d+ \d+/', $header_value ); | ||
| }, | ||
| ); | ||
|
|
||
| /** | ||
| * Filters the list of cache headers supported by core. | ||
| * | ||
| * @since 6.1.0 | ||
| * | ||
| * @param array $cache_headers Array of supported cache headers. | ||
| * @param array<string, ?callable> $cache_headers Mapping of page caching headers and their (optional) verification callbacks. | ||
| */ | ||
| return apply_filters( 'site_status_page_cache_supported_cache_headers', $cache_headers ); | ||
| return (array) apply_filters( 'site_status_page_cache_supported_cache_headers', $cache_headers ); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,10 +12,8 @@ class Tests_Admin_wpSiteHealth extends WP_UnitTestCase { | |
| * An instance of the class to test. | ||
| * | ||
| * @since 6.1.0 | ||
| * | ||
| * @var WP_Site_Health | ||
| */ | ||
| private $instance; | ||
| private WP_Site_Health $instance; | ||
|
|
||
| public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { | ||
| // Include the `WP_Site_Health` file. | ||
|
|
@@ -172,7 +170,7 @@ public function data_cron_health_checks() { | |
| * @covers ::get_page_cache_headers() | ||
| * @covers ::check_for_page_caching() | ||
| */ | ||
| public function test_get_page_cache( $responses, $expected_status, $expected_label, $good_basic_auth = null, $delay_the_response = false ) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that |
||
| public function test_get_page_cache( array $responses, string $expected_status, string $expected_label, bool $has_basic_auth = false, bool $delay_the_response = false ) { | ||
| $expected_props = array( | ||
| 'badge' => array( | ||
| 'label' => __( 'Performance' ), | ||
|
|
@@ -183,7 +181,7 @@ public function test_get_page_cache( $responses, $expected_status, $expected_lab | |
| 'label' => $expected_label, | ||
| ); | ||
|
|
||
| if ( null !== $good_basic_auth ) { | ||
| if ( $has_basic_auth ) { | ||
| $_SERVER['PHP_AUTH_USER'] = 'admin'; | ||
| $_SERVER['PHP_AUTH_PW'] = 'password'; | ||
| } | ||
|
|
@@ -200,7 +198,7 @@ static function () use ( $threshold ) { | |
|
|
||
| add_filter( | ||
| 'pre_http_request', | ||
| function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $good_basic_auth, $delay_the_response, $threshold ) { | ||
| function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $has_basic_auth, $delay_the_response, $threshold ) { | ||
|
|
||
| $expected_response = array_shift( $responses ); | ||
|
|
||
|
|
@@ -219,7 +217,7 @@ function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $good | |
| ); | ||
| } | ||
|
|
||
| if ( null !== $good_basic_auth ) { | ||
| if ( $has_basic_auth ) { | ||
| $this->assertArrayHasKey( | ||
| 'Authorization', | ||
| $parsed_args['headers'] | ||
|
|
@@ -263,9 +261,15 @@ function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $good | |
| * | ||
| * @ticket 56041 | ||
| * | ||
| * @return array[] | ||
| * @return array<string, array{ | ||
| * responses: array<int, string|array<string, string|string[]>>, | ||
| * expected_status: 'recommended'|'critical'|'good', | ||
| * expected_label: string, | ||
| * good_basic_auth?: bool, | ||
| * delay_the_response?: bool, | ||
| * }> | ||
| */ | ||
| public function data_get_page_cache() { | ||
| public function data_get_page_cache(): array { | ||
| $recommended_label = 'Page cache is not detected but the server response time is OK'; | ||
| $good_label = 'Page cache is detected and the server response time is good'; | ||
| $critical_label = 'Page cache is not detected and the server response time is slow'; | ||
|
|
@@ -278,13 +282,13 @@ public function data_get_page_cache() { | |
| ), | ||
| 'expected_status' => 'recommended', | ||
| 'expected_label' => $error_label, | ||
| 'good_basic_auth' => false, | ||
| 'has_basic_auth' => true, | ||
| ), | ||
| 'no-cache-control' => array( | ||
| 'responses' => array_fill( 0, 3, array() ), | ||
| 'expected_status' => 'critical', | ||
| 'expected_label' => $critical_label, | ||
| 'good_basic_auth' => null, | ||
| 'has_basic_auth' => false, | ||
| 'delay_the_response' => true, | ||
| ), | ||
| 'no-cache' => array( | ||
|
|
@@ -310,7 +314,7 @@ public function data_get_page_cache() { | |
| 'responses' => array_fill( 0, 3, array( 'cache-control' => 'no-cache' ) ), | ||
| 'expected_status' => 'critical', | ||
| 'expected_label' => $critical_label, | ||
| 'good_basic_auth' => null, | ||
| 'has_basic_auth' => false, | ||
| 'delay_the_response' => true, | ||
| ), | ||
| 'age' => array( | ||
|
|
@@ -366,7 +370,7 @@ public function data_get_page_cache() { | |
| ), | ||
| 'expected_status' => 'critical', | ||
| 'expected_label' => $critical_label, | ||
| 'good_basic_auth' => null, | ||
| 'has_basic_auth' => false, | ||
| 'delay_the_response' => true, | ||
| ), | ||
| 'cache-control-with-basic-auth' => array( | ||
|
|
@@ -377,7 +381,7 @@ public function data_get_page_cache() { | |
| ), | ||
| 'expected_status' => 'good', | ||
| 'expected_label' => $good_label, | ||
| 'good_basic_auth' => true, | ||
| 'has_basic_auth' => true, | ||
| ), | ||
| 'x-cache-enabled' => array( | ||
| 'responses' => array_fill( | ||
|
|
@@ -396,7 +400,7 @@ public function data_get_page_cache() { | |
| ), | ||
| 'expected_status' => 'critical', | ||
| 'expected_label' => $critical_label, | ||
| 'good_basic_auth' => null, | ||
| 'has_basic_auth' => false, | ||
| 'delay_the_response' => true, | ||
| ), | ||
| 'x-cache-disabled' => array( | ||
|
|
@@ -408,6 +412,78 @@ public function data_get_page_cache() { | |
| 'expected_status' => 'good', | ||
| 'expected_label' => $good_label, | ||
| ), | ||
| 'false-positive-hit-in-word' => array( | ||
| 'responses' => array_fill( | ||
| 0, | ||
| 3, | ||
| array( 'x-cache' => 'no-hit' ) | ||
| ), | ||
| 'expected_status' => 'recommended', | ||
| 'expected_label' => $recommended_label, | ||
| ), | ||
| 'varnish-header' => array( | ||
| 'responses' => array_fill( | ||
| 0, | ||
| 3, | ||
| array( 'x-varnish' => '123 456' ) | ||
| ), | ||
| 'expected_status' => 'good', | ||
| 'expected_label' => $good_label, | ||
| ), | ||
| 'varnish-header-miss' => array( | ||
| 'responses' => array_fill( | ||
| 0, | ||
| 3, | ||
| array( 'x-varnish' => '123' ) | ||
| ), | ||
| 'expected_status' => 'recommended', | ||
| 'expected_label' => $recommended_label, | ||
| ), | ||
| 'srcache-store-status' => array( | ||
| 'responses' => array_fill( | ||
| 0, | ||
| 3, | ||
| array( 'x-srcache-store-status' => 'STORE' ) | ||
| ), | ||
| 'expected_status' => 'good', | ||
| 'expected_label' => $good_label, | ||
| ), | ||
| 'srcache-store-status-bypass' => array( | ||
| 'responses' => array_fill( | ||
| 0, | ||
| 3, | ||
| array( 'x-srcache-store-status' => 'BYPASS' ) | ||
| ), | ||
| 'expected_status' => 'recommended', | ||
| 'expected_label' => $recommended_label, | ||
| ), | ||
| 'srcache-fetch-status' => array( | ||
| 'responses' => array_fill( | ||
| 0, | ||
| 3, | ||
| array( 'x-srcache-fetch-status' => 'HIT' ) | ||
| ), | ||
| 'expected_status' => 'good', | ||
| 'expected_label' => $good_label, | ||
| ), | ||
| 'last-modified' => array( | ||
| 'responses' => array_fill( | ||
| 0, | ||
| 3, | ||
| array( 'last-modified' => 'Wed, 21 Oct 2015 07:28:00 GMT' ) | ||
| ), | ||
| 'expected_status' => 'good', | ||
| 'expected_label' => $good_label, | ||
| ), | ||
| 'via' => array( | ||
| 'responses' => array_fill( | ||
| 0, | ||
| 3, | ||
| array( 'via' => '1.1 varnish' ) | ||
| ), | ||
| 'expected_status' => 'good', | ||
| 'expected_label' => $good_label, | ||
| ), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because I’m not familiar with all of these systems, and don’t have any in front of me, I am not reviewing or verifying these tests. I’m trusting that you confirmed that the responses correspond to what those projects actually produce and that the values here are factually representative. the CommonCrawl archive would potentially be a valuable archive to mine for this kind of header behavior. |
||
| ); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the previous comment explained what an empty value means, but this change removes that knowledge. was that intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it was redundant, but it seems it isn't. How about this: