From ce38b9c90ae88513de95562328eb80e60a73b2ae Mon Sep 17 00:00:00 2001 From: James Nylen Date: Fri, 19 May 2017 20:26:48 +0000 Subject: [PATCH] REST API: Avoid sending blank `Last-Modified` headers with authenticated requests. This commit adds a new `WP_REST_Server#remove_header` method and uses it to clear the `Last-Modified` header when the "no caching" headers are sent (by default for all authenticated REST API requests). This matches the behavior of the `nocache_headers` function used in other parts of WordPress. Previously, the REST API would send an empty `Last-Modified` header in this situation. Under some server and browser configurations, this causes browsers to cache authenticated REST API requests, which is undesirable. Props iv3rson76, zinigor, rmccue, jnylen0. Fixes #40444. git-svn-id: https://develop.svn.wordpress.org/trunk@40805 602fd350-edb4-49c9-b593-d223f7449a82 --- .../rest-api/class-wp-rest-server.php | 30 ++++++++++++++++++- tests/phpunit/includes/spy-rest-server.php | 4 +++ tests/phpunit/tests/rest-api/rest-server.php | 7 +++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/class-wp-rest-server.php b/src/wp-includes/rest-api/class-wp-rest-server.php index 9d4a5fec05..11b8897a2b 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -252,7 +252,11 @@ class WP_REST_Server { $send_no_cache_headers = apply_filters( 'rest_send_nocache_headers', is_user_logged_in() ); if ( $send_no_cache_headers ) { foreach ( wp_get_nocache_headers() as $header => $header_value ) { - $this->send_header( $header, $header_value ); + if ( empty( $header_value ) ) { + $this->remove_header( $header ); + } else { + $this->send_header( $header, $header_value ); + } } } @@ -1262,6 +1266,30 @@ class WP_REST_Server { } } + /** + * Removes an HTTP header from the current response. + * + * @since 4.8.0 + * @access public + * + * @param string $key Header key. + */ + public function remove_header( $key ) { + if ( function_exists( 'header_remove' ) ) { + // In PHP 5.3+ there is a way to remove an already set header. + header_remove( $key ); + } else { + // In PHP 5.2, send an empty header, but only as a last resort to + // override a header already sent. + foreach ( headers_list() as $header ) { + if ( 0 === stripos( $header, "$key:" ) ) { + $this->send_header( $key, '' ); + break; + } + } + } + } + /** * Retrieves the raw request entity (body). * diff --git a/tests/phpunit/includes/spy-rest-server.php b/tests/phpunit/includes/spy-rest-server.php index 58e3c3b521..b14e2ab7d1 100644 --- a/tests/phpunit/includes/spy-rest-server.php +++ b/tests/phpunit/includes/spy-rest-server.php @@ -31,6 +31,10 @@ class Spy_REST_Server extends WP_REST_Server { $this->sent_headers[ $header ] = $value; } + public function remove_header( $header ) { + unset( $this->sent_headers[ $header ] ); + } + /** * Override the dispatch method so we can get a handle on the request object. * diff --git a/tests/phpunit/tests/rest-api/rest-server.php b/tests/phpunit/tests/rest-api/rest-server.php index 046d2b5f62..1cd9e68463 100644 --- a/tests/phpunit/tests/rest-api/rest-server.php +++ b/tests/phpunit/tests/rest-api/rest-server.php @@ -763,9 +763,16 @@ class Tests_REST_Server extends WP_Test_REST_TestCase { $headers = $this->server->sent_headers; foreach ( wp_get_nocache_headers() as $header => $value ) { + if ( empty( $value ) ) { + continue; + } + $this->assertTrue( isset( $headers[ $header ] ), sprintf( 'Header %s is not present in the response.', $header ) ); $this->assertEquals( $value, $headers[ $header ] ); } + + // Last-Modified should be unset as per #WP23021 + $this->assertFalse( isset( $headers['Last-Modified'] ), 'Last-Modified should not be sent.' ); } public function test_no_nocache_headers_on_unauthenticated_requests() {