From b80fd51601bb95ea2ddc04ea98b7e4d79f68ac8c Mon Sep 17 00:00:00 2001 From: Anton Vlasenko Date: Sat, 14 Dec 2024 02:50:33 +0100 Subject: [PATCH] Don't prepare the response body for HEAD requests in WP_Test_REST_Post_Types_Controller. --- .../class-wp-rest-post-types-controller.php | 9 +++ .../rest-api/rest-post-types-controller.php | 76 +++++++++++++++++-- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php index d766d5c61ddf8..bc4d5e8bd18bb 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php @@ -109,6 +109,11 @@ public function get_items_permissions_check( $request ) { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_items( $request ) { + if ( $request->is_method( 'HEAD' ) ) { + // Return early as this handler doesn't add any response headers. + return new WP_REST_Response(); + } + $data = array(); $types = get_post_types( array( 'show_in_rest' => true ), 'objects' ); @@ -159,6 +164,10 @@ public function get_item( $request ) { ); } + if ( $request->is_method( 'HEAD' ) ) { + return new WP_REST_Response(); + } + $data = $this->prepare_item_for_response( $obj, $request ); return rest_ensure_response( $data ); diff --git a/tests/phpunit/tests/rest-api/rest-post-types-controller.php b/tests/phpunit/tests/rest-api/rest-post-types-controller.php index 8967a507063a1..be828dc230eae 100644 --- a/tests/phpunit/tests/rest-api/rest-post-types-controller.php +++ b/tests/phpunit/tests/rest-api/rest-post-types-controller.php @@ -44,14 +44,32 @@ public function test_get_items() { $this->assertArrayNotHasKey( 'revision', $data ); } - public function test_get_items_invalid_permission_for_context() { + /** + * @dataProvider data_readable_http_methods + * @ticket 56481 + * + * @param string $method HTTP method to use. + */ + public function test_get_items_invalid_permission_for_context( $method ) { wp_set_current_user( 0 ); - $request = new WP_REST_Request( 'GET', '/wp/v2/types' ); + $request = new WP_REST_Request( $method, '/wp/v2/types' ); $request->set_param( 'context', 'edit' ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_view', $response, 401 ); } + /** + * Data provider intended to provide HTTP method names for testing GET and HEAD requests. + * + * @return array + */ + public static function data_readable_http_methods() { + return array( + 'GET request' => array( 'GET' ), + 'HEAD request' => array( 'HEAD' ), + ); + } + public function test_get_item() { $request = new WP_REST_Request( 'GET', '/wp/v2/types/post' ); $response = rest_get_server()->dispatch( $request ); @@ -60,6 +78,24 @@ public function test_get_item() { $this->assertSame( array( 'category', 'post_tag' ), $data['taxonomies'] ); } + /** + * @ticket 56481 + */ + public function test_get_item_with_head_request_should_not_prepare_post_type_data() { + $request = new WP_REST_Request( 'HEAD', '/wp/v2/types/post' ); + $hook_name = 'rest_prepare_post_type'; + $filter = new MockAction(); + $callback = array( $filter, 'filter' ); + add_filter( $hook_name, $callback ); + $response = rest_get_server()->dispatch( $request ); + remove_filter( $hook_name, $callback ); + + $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' ); + + $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' ); + $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' ); + } + /** * @ticket 53656 */ @@ -106,8 +142,14 @@ public function test_get_item_page() { $this->assertSame( array(), $data['taxonomies'] ); } - public function test_get_item_invalid_type() { - $request = new WP_REST_Request( 'GET', '/wp/v2/types/invalid' ); + /** + * @dataProvider data_readable_http_methods + * @ticket 56481 + * + * @param string $method HTTP method to use. + */ + public function test_get_item_invalid_type( $method ) { + $request = new WP_REST_Request( $method, '/wp/v2/types/invalid' ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_type_invalid', $response, 404 ); } @@ -121,9 +163,15 @@ public function test_get_item_edit_context() { $this->check_post_type_object_response( 'edit', $response ); } - public function test_get_item_invalid_permission_for_context() { + /** + * @dataProvider data_readable_http_methods + * @ticket 56481 + * + * @param string $method HTTP method to use. + */ + public function test_get_item_invalid_permission_for_context( $method ) { wp_set_current_user( 0 ); - $request = new WP_REST_Request( 'GET', '/wp/v2/types/post' ); + $request = new WP_REST_Request( $method, '/wp/v2/types/post' ); $request->set_param( 'context', 'edit' ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_forbidden_context', $response, 401 ); @@ -245,6 +293,22 @@ public function additional_field_get_callback( $response_data ) { return 123; } + /** + * @ticket 56481 + */ + public function test_get_items_with_head_request_should_not_prepare_post_types_data() { + $request = new WP_REST_Request( 'HEAD', '/wp/v2/types' ); + $hook_name = 'rest_prepare_post_type'; + $filter = new MockAction(); + $callback = array( $filter, 'filter' ); + add_filter( $hook_name, $callback ); + $response = rest_get_server()->dispatch( $request ); + remove_filter( $hook_name, $callback ); + $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' ); + $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' ); + $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' ); + } + protected function check_post_type_obj( $context, $post_type_obj, $data, $links ) { $this->assertSame( $post_type_obj->label, $data['name'] ); $this->assertSame( $post_type_obj->name, $data['slug'] );