Skip to content

Commit

Permalink
Move the check for HEAD requests to ::prepare_item_for_response() met…
Browse files Browse the repository at this point in the history
…hod to preserve BC.
  • Loading branch information
anton-vlasenko committed Dec 20, 2024
1 parent b80fd51 commit c9c4d20
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,6 @@ public function get_item( $request ) {
return $comment;
}

if ( $request->is_method( 'HEAD' ) ) {
// Don't prepare response body for HEAD requests.
return new WP_REST_Response();
}

$data = $this->prepare_item_for_response( $comment, $request );
$response = rest_ensure_response( $data );

Expand Down Expand Up @@ -1056,6 +1051,12 @@ public function prepare_item_for_response( $item, $request ) {
// Restores the more descriptive, specific name for use within this method.
$comment = $item;

// Don't prepare the response body for HEAD requests.
if ( $request->is_method( 'HEAD' ) ) {
/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php */
return apply_filters( 'rest_prepare_comment', new WP_REST_Response(), $comment, $request );
}

$fields = $this->get_fields_for_response( $request );
$data = array();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,6 @@ 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 );
Expand All @@ -187,6 +183,12 @@ public function prepare_item_for_response( $item, $request ) {
// Restores the more descriptive, specific name for use within this method.
$post_type = $item;

// Don't prepare the response body for HEAD requests.
if ( $request->is_method( 'HEAD' ) ) {
/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php */
return apply_filters( 'rest_prepare_post_type', new WP_REST_Response(), $post_type, $request );
}

$taxonomies = wp_list_filter( get_object_taxonomies( $post_type->name, 'objects' ), array( 'show_in_rest' => true ) );
$taxonomies = wp_list_pluck( $taxonomies, 'name' );
$base = ! empty( $post_type->rest_base ) ? $post_type->rest_base : $post_type->name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,12 +643,8 @@ public function get_item( $request ) {
return $post;
}

if ( $request->is_method( 'HEAD' ) ) {
$response = new WP_REST_Response();
} else {
$data = $this->prepare_item_for_response( $post, $request );
$response = rest_ensure_response( $data );
}
$data = $this->prepare_item_for_response( $post, $request );
$response = rest_ensure_response( $data );

if ( is_post_type_viewable( get_post_type_object( $post->post_type ) ) ) {
$response->link_header( 'alternate', get_permalink( $post->ID ), array( 'type' => 'text/html' ) );
Expand Down Expand Up @@ -1839,6 +1835,12 @@ public function prepare_item_for_response( $item, $request ) {

setup_postdata( $post );

// Don't prepare the response body for HEAD requests.
if ( $request->is_method( 'HEAD' ) ) {
/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
return apply_filters( "rest_prepare_{$this->post_type}", new WP_REST_Response(), $post, $request );
}

$fields = $this->get_fields_for_response( $request );

// Base fields for every post.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,6 @@ public function get_item( $request ) {
);
}

if ( $request->is_method( 'HEAD' ) ) {
return new WP_REST_Response();
}

$data = $this->prepare_item_for_response( $tax_obj, $request );

return rest_ensure_response( $data );
Expand All @@ -218,6 +214,12 @@ public function prepare_item_for_response( $item, $request ) {
// Restores the more descriptive, specific name for use within this method.
$taxonomy = $item;

// Don't prepare the response body for HEAD requests.
if ( $request->is_method( 'HEAD' ) ) {
/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php */
return apply_filters( 'rest_prepare_taxonomy', new WP_REST_Response(), $taxonomy, $request );
}

$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;

$fields = $this->get_fields_for_response( $request );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,10 +477,6 @@ public function get_item( $request ) {
return $term;
}

if ( $request->is_method( 'HEAD' ) ) {
return new WP_REST_Response();
}

$response = $this->prepare_item_for_response( $term, $request );

return rest_ensure_response( $response );
Expand Down Expand Up @@ -900,6 +896,12 @@ public function prepare_item_for_database( $request ) {
*/
public function prepare_item_for_response( $item, $request ) {

// Don't prepare the response body for HEAD requests.
if ( $request->is_method( 'HEAD' ) ) {
/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */
return apply_filters( "rest_prepare_{$this->taxonomy}", new WP_REST_Response(), $item, $request );
}

$fields = $this->get_fields_for_response( $request );
$data = array();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,10 +488,6 @@ public function get_item( $request ) {
return $user;
}

if ( $request->is_method( 'HEAD' ) ) {
return new WP_REST_Response();
}

$user = $this->prepare_item_for_response( $user, $request );
$response = rest_ensure_response( $user );

Expand Down Expand Up @@ -1011,6 +1007,12 @@ public function prepare_item_for_response( $item, $request ) {
// Restores the more descriptive, specific name for use within this method.
$user = $item;

// Don't prepare the response body for HEAD requests.
if ( $request->is_method( 'HEAD' ) ) {
/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php */
return apply_filters( 'rest_prepare_user', new WP_REST_Response(), $user, $request );
}

$fields = $this->get_fields_for_response( $request );
$data = array();

Expand Down
25 changes: 21 additions & 4 deletions tests/phpunit/tests/rest-api/rest-categories-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -1325,24 +1325,41 @@ public static function data_readable_http_methods() {
}

/**
* @dataProvider data_readable_http_methods
* @ticket 56481
*
* @param string $method The HTTP method to use.
*/
public function test_get_item_with_head_request_should_not_prepare_category_data() {
public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
$category_id = self::factory()->category->create();

$request = new WP_REST_Request( 'HEAD', sprintf( '/wp/v2/categories/%d', $category_id ) );
$request = new WP_REST_Request( $method, sprintf( '/wp/v2/categories/%d', $category_id ) );

$hook_name = 'rest_prepare_category';

$filter = new MockAction();
$callback = array( $filter, 'filter' );
add_filter( $hook_name, $callback );
$header_filter = new class() {
public static function add_custom_header( $response ) {
$response->header( 'X-Test-Header', 'Test' );

return $response;
}
};
add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
$response = rest_get_server()->dispatch( $request );
remove_filter( $hook_name, $callback );
remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );

$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->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
$headers = $response->get_headers();
$this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
$this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
if ( 'HEAD' !== $method ) {
return null;
}
$this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
}
}
24 changes: 21 additions & 3 deletions tests/phpunit/tests/rest-api/rest-comments-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -3534,21 +3534,39 @@ public function test_get_items_only_fetches_ids_for_head_requests( $method ) {
}

/**
* @dataProvider data_readable_http_methods
* @ticket 56481
*
* @param string $method The HTTP method to use.
*/
public function test_get_item_with_head_request_should_not_prepare_comment_data() {
$request = new WP_REST_Request( 'HEAD', sprintf( '/wp/v2/comments/%d', self::$approved_id ) );
public function test_get_item_with_head_request_should_not_prepare_comment_data( $method ) {
$request = new WP_REST_Request( $method, sprintf( '/wp/v2/comments/%d', self::$approved_id ) );

$hook_name = 'rest_prepare_comment';

$filter = new MockAction();
$callback = array( $filter, 'filter' );
add_filter( $hook_name, $callback );
$header_filter = new class() {
public static function add_custom_header( $response ) {
$response->header( 'X-Test-Header', 'Test' );

return $response;
}
};
add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
$response = rest_get_server()->dispatch( $request );
remove_filter( $hook_name, $callback );
remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );

$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.' );
$headers = $response->get_headers();
$this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
$this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
$this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
if ( 'HEAD' !== $method ) {
return null;
}
$this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
}
}
26 changes: 22 additions & 4 deletions tests/phpunit/tests/rest-api/rest-post-types-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,38 @@ public function test_get_item() {
}

/**
* @dataProvider data_readable_http_methods
* @ticket 56481
*
* @param string $method The HTTP method to use.
*/
public function test_get_item_with_head_request_should_not_prepare_post_type_data() {
$request = new WP_REST_Request( 'HEAD', '/wp/v2/types/post' );
public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
$request = new WP_REST_Request( $method, '/wp/v2/types/post' );

$hook_name = 'rest_prepare_post_type';
$filter = new MockAction();
$callback = array( $filter, 'filter' );
add_filter( $hook_name, $callback );
$header_filter = new class() {
public static function add_custom_header( $response ) {
$response->header( 'X-Test-Header', 'Test' );

return $response;
}
};
add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
$response = rest_get_server()->dispatch( $request );
remove_filter( $hook_name, $callback );
remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );

$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->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
$headers = $response->get_headers();
$this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
$this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
if ( 'HEAD' !== $method ) {
return null;
}
$this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
}

Expand Down
29 changes: 22 additions & 7 deletions tests/phpunit/tests/rest-api/rest-posts-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2159,24 +2159,39 @@ public function test_get_item() {
}

/**
* @dataProvider data_readable_http_methods
* @ticket 56481
*
* @param string $method The HTTP method to use.
*/
public function test_get_item_with_head_request_should_not_prepare_post_data() {
$request = new WP_REST_Request( 'HEAD', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
$request = new WP_REST_Request( $method, sprintf( '/wp/v2/posts/%d', self::$post_id ) );

$hook_name = 'rest_prepare_' . get_post_type( self::$post_id );

$filter = new MockAction();
$callback = array( $filter, 'filter' );
$filter = new MockAction();
$callback = array( $filter, 'filter' );
add_filter( $hook_name, $callback );
$header_filter = new class() {
public static function add_custom_header( $response ) {
$response->header( 'X-Test-Header', 'Test' );

return $response;
}
};
add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
$response = rest_get_server()->dispatch( $request );
remove_filter( $hook_name, $callback );
remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );

$this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );

$this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
$headers = $response->get_headers();
$this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
$this->assertArrayHasKey( 'Link', $headers, 'The "Link" header should be present in the response.' );
$this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
$this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
if ( 'HEAD' !== $method ) {
return null;
}
$this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
}

Expand Down
25 changes: 21 additions & 4 deletions tests/phpunit/tests/rest-api/rest-tags-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -1579,24 +1579,41 @@ public static function data_readable_http_methods() {
}

/**
* @dataProvider data_readable_http_methods
* @ticket 56481
*
* @param string $method The HTTP method to use.
*/
public function test_get_item_with_head_request_should_not_prepare_tag_data() {
public function test_get_item_should_allow_adding_headers_via_filter( string $method ) {
$tag_id = self::factory()->tag->create();

$request = new WP_REST_Request( 'HEAD', sprintf( '/wp/v2/tags/%d', $tag_id ) );
$request = new WP_REST_Request( $method, sprintf( '/wp/v2/tags/%d', $tag_id ) );

$hook_name = 'rest_prepare_post_tag';

$filter = new MockAction();
$callback = array( $filter, 'filter' );
add_filter( $hook_name, $callback );
$header_filter = new class() {
public static function add_custom_header( $response ) {
$response->header( 'X-Test-Header', 'Test' );

return $response;
}
};
add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
$response = rest_get_server()->dispatch( $request );
remove_filter( $hook_name, $callback );
remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );

$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->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
$headers = $response->get_headers();
$this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
$this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
if ( 'HEAD' !== $method ) {
return null;
}
$this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
}
}
Loading

0 comments on commit c9c4d20

Please sign in to comment.