Skip to content
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

Don't prepare the response body for HEAD requests #7970

Open
wants to merge 51 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
955a3a4
Short circuit the WP_REST_Posts_Controller controller.
anton-vlasenko Dec 7, 2024
48a4b07
Short circuit the WP_REST_Comments_Controller controller.
anton-vlasenko Dec 7, 2024
d9b13f2
Short circuit the WP_REST_Terms_Controller controller.
anton-vlasenko Dec 7, 2024
301fa88
Short circuit the WP_REST_Users_Controller controller.
anton-vlasenko Dec 7, 2024
6cdcf6b
Short circuit the WP_REST_Taxonomies_Controller controller.
anton-vlasenko Dec 7, 2024
bbcb099
Use the upper-case http method name (per https://core.trac.wordpress.…
anton-vlasenko Dec 7, 2024
79c6143
Add PHPUnit tests for the WP_Request::is_method() method.
anton-vlasenko Dec 7, 2024
6db28eb
Better test method name.
anton-vlasenko Dec 7, 2024
8fa6aa4
Rename the test methods.
anton-vlasenko Dec 9, 2024
98c03bb
Data providers should always be declared as static. Props @swisspidy.
anton-vlasenko Dec 12, 2024
dd8a5ec
Disable priming meta for HEAD requests to improve performance.
anton-vlasenko Dec 13, 2024
19074f9
Fix CS and typos.
anton-vlasenko Dec 14, 2024
3de5947
Don't prepare the response body for HEAD requests in WP_Test_REST_Pos…
anton-vlasenko Dec 14, 2024
ce06631
Move the check for HEAD requests to ::prepare_item_for_response() met…
anton-vlasenko Dec 17, 2024
77d7c92
Short circuit the WP_REST_Block_Pattern_Categories_Controller control…
anton-vlasenko Dec 20, 2024
b9360fd
Short circuit the WP_REST_Block_Types_Controller controller.
anton-vlasenko Dec 22, 2024
1063c67
Fix CS.
anton-vlasenko Dec 22, 2024
5512e09
Add unit tests.
anton-vlasenko Dec 23, 2024
e81fac3
Short circuit the WP_REST_Font_Collections_Controller controller.
anton-vlasenko Dec 23, 2024
0cd6883
Add unit tests.
anton-vlasenko Dec 23, 2024
02aec99
Short circuit the WP_REST_Revisions_Controller controller.
anton-vlasenko Dec 26, 2024
fc1460b
Add unit tests.
anton-vlasenko Dec 26, 2024
4627401
Short circuit the WP_REST_Global_Styles_Revisions_Controller controller.
anton-vlasenko Dec 26, 2024
f4a2af4
Short circuit the WP_REST_Global_Styles_Revisions_Controller controller.
anton-vlasenko Dec 26, 2024
a0bc222
Add unit tests.
anton-vlasenko Dec 26, 2024
4000e22
Fix the failing unit test.
anton-vlasenko Dec 26, 2024
5ce28ef
Add unit tests.
anton-vlasenko Dec 27, 2024
05da914
Short circuit the WP_REST_Search_Controller controller.
anton-vlasenko Dec 27, 2024
4dc9092
Fix the WP_Test_REST_Posts_Controller::test_get_items_pagination_head…
anton-vlasenko Dec 27, 2024
bdd97cf
Short circuit the WP_REST_Sidebars_Controller controller.
anton-vlasenko Dec 27, 2024
6772204
Remove non-existent test method parameter.
anton-vlasenko Dec 27, 2024
e0c6424
Add unit tests.
anton-vlasenko Dec 27, 2024
f79d0dd
Fix backward compatibility issues in the WP_REST_Font_Collections_Con…
anton-vlasenko Dec 27, 2024
be26c39
Short circuit the WP_REST_Templates_Controller controller.
anton-vlasenko Dec 27, 2024
8c384d4
Add unit tests.
anton-vlasenko Dec 27, 2024
eb7d4d2
Short circuit the WP_REST_Widget_Types_Controller controller.
anton-vlasenko Dec 27, 2024
25b39a3
Fix the unit test.
anton-vlasenko Dec 30, 2024
792128f
Add unit tests.
anton-vlasenko Dec 30, 2024
bd9bae3
Short circuit the WP_REST_Widgets_Controller controller.
anton-vlasenko Dec 30, 2024
e6ed30f
Add unit tests.
anton-vlasenko Dec 30, 2024
61010ec
Unify inline comments.
anton-vlasenko Dec 30, 2024
dccc30d
Short circuit the WP_REST_Autosaves_Controller controller.
janusqa Dec 30, 2024
1fbaeb4
Add unit tests.
anton-vlasenko Dec 30, 2024
85ce233
Short-circuit the WP_REST_Template_Autosaves_Controller and WP_REST_T…
anton-vlasenko Dec 31, 2024
9d4b831
Rename test methods.
anton-vlasenko Dec 31, 2024
cb42502
Add unit tests.
anton-vlasenko Dec 31, 2024
021c2c5
Add unit tests.
anton-vlasenko Dec 31, 2024
72a3a1e
Short circuit the WP_REST_Pattern_Directory_Controller controller.
anton-vlasenko Jan 2, 2025
4765127
Update src/wp-includes/rest-api/endpoints/class-wp-rest-font-collecti…
anton-vlasenko Jan 6, 2025
fa48424
Fix code style.
anton-vlasenko Jan 15, 2025
fd60b44
Fix the failing PHPUnit tests.
anton-vlasenko Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/wp-includes/rest-api/class-wp-rest-request.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,18 @@ public function get_headers() {
return $this->headers;
}

/**
* Determines if the request is the given method.
*
* @since 6.8.0
*
* @param string $method HTTP method.
* @return bool Whether the request is of the given method.
*/
public function is_method( $method ) {
return $this->get_method() === strtoupper( $method );
}

/**
* Canonicalizes the header name.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ public function get_items( $request ) {
return $parent;
}

if ( $request->is_method( 'HEAD' ) ) {
// Return early as this handler doesn't add any response headers.
return new WP_REST_Response();
}
$response = array();
$parent_id = $parent->ID;
$revisions = wp_get_post_revisions( $parent_id, array( 'check_enabled' => false ) );
Expand Down Expand Up @@ -448,6 +452,11 @@ public function prepare_item_for_response( $item, $request ) {
// Restores the more descriptive, specific name for use within this method.
$post = $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-autosaves-controller.php */
return apply_filters( 'rest_prepare_autosave', new WP_REST_Response(), $post, $request );
}
$response = $this->revisions_controller->prepare_item_for_response( $post, $request );
$fields = $this->get_fields_for_response( $request );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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();
}

$response = array();
$categories = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered();
foreach ( $categories as $category ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,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();
$block_types = $this->block_registry->get_all_registered();

Expand Down Expand Up @@ -250,6 +255,12 @@ public function prepare_item_for_response( $item, $request ) {
// Restores the more descriptive, specific name for use within this method.
$block_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-block-types-controller.php */
return apply_filters( 'rest_prepare_block_type', new WP_REST_Response(), $block_type, $request );
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,14 @@ public function get_items( $request ) {
$prepared_args['offset'] = $prepared_args['number'] * ( absint( $request['page'] ) - 1 );
}

$is_head_request = $request->is_method( 'HEAD' );
if ( $is_head_request ) {
// Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.
$prepared_args['fields'] = 'ids';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we avoid priming comment meta here as well?

Copy link
Author

@anton-vlasenko anton-vlasenko Dec 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we avoid priming comment meta here as well?

Yes, I think we should avoid priming comment meta here.
Fixed in 294584e

// Disable priming comment meta for HEAD requests to improve performance.
$prepared_args['update_comment_meta_cache'] = false;
}

/**
* Filters WP_Comment_Query arguments when querying comments via the REST API.
*
Expand All @@ -277,15 +285,17 @@ public function get_items( $request ) {
$query = new WP_Comment_Query();
$query_result = $query->query( $prepared_args );

$comments = array();
if ( ! $is_head_request ) {
$comments = array();

foreach ( $query_result as $comment ) {
if ( ! $this->check_read_permission( $comment, $request ) ) {
continue;
}
foreach ( $query_result as $comment ) {
if ( ! $this->check_read_permission( $comment, $request ) ) {
continue;
}

$data = $this->prepare_item_for_response( $comment, $request );
$comments[] = $this->prepare_response_for_collection( $data );
$data = $this->prepare_item_for_response( $comment, $request );
$comments[] = $this->prepare_response_for_collection( $data );
}
}

$total_comments = (int) $query->found_comments;
Expand All @@ -303,7 +313,7 @@ public function get_items( $request ) {
$max_pages = (int) ceil( $total_comments / $request['per_page'] );
}

$response = rest_ensure_response( $comments );
$response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $comments );
$response->header( 'X-WP-Total', $total_comments );
$response->header( 'X-WP-TotalPages', $max_pages );

Expand Down Expand Up @@ -1041,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 @@ -89,6 +89,8 @@ public function get_items( $request ) {

$collections_page = array_slice( $collections_all, ( $page - 1 ) * $per_page, $per_page );

$is_head_request = $request->is_method( 'HEAD' );

$items = array();
foreach ( $collections_page as $collection ) {
$item = $this->prepare_item_for_response( $collection, $request );
Expand All @@ -97,11 +99,21 @@ public function get_items( $request ) {
if ( is_wp_error( $item ) ) {
continue;
}

/*
* Skip preparing the response body for HEAD requests.
* Cannot exit earlier due to backward compatibility reasons,
* as validation occurs in the prepare_item_for_response method.
*/
if ( $is_head_request ) {
continue;
}

$item = $this->prepare_response_for_collection( $item );
$items[] = $item;
}

$response = rest_ensure_response( $items );
$response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $items );

$response->header( 'X-WP-Total', (int) $total_items );
$response->header( 'X-WP-TotalPages', $max_pages );
Expand Down Expand Up @@ -175,13 +187,31 @@ public function prepare_item_for_response( $item, $request ) {
return $collection_data;
}

/**
* Don't prepare the response body for HEAD requests.
* Can't exit at the beginning of the method due to the potential need to return a WP_Error object.
*/
if ( $request->is_method( 'HEAD' ) ) {
/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-font-collections-controller.php */
return apply_filters( 'rest_prepare_font_collection', new WP_REST_Response(), $item, $request );
}

foreach ( $data_fields as $field ) {
if ( rest_is_field_included( $field, $fields ) ) {
$data[ $field ] = $collection_data[ $field ];
}
}
}

/**
* Don't prepare the response body for HEAD requests.
* Can't exit at the beginning of the method due to the potential need to return a WP_Error object.
*/
if ( $request->is_method( 'HEAD' ) ) {
/** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-font-collections-controller.php */
return apply_filters( 'rest_prepare_font_collection', new WP_REST_Response(), $item, $request );
}

$response = rest_ensure_response( $data );

if ( rest_is_field_included( '_links', $fields ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ public function get_items( $request ) {
return $global_styles_config;
}

$is_head_request = $request->is_method( 'HEAD' );

if ( wp_revisions_enabled( $parent ) ) {
$registered = $this->get_collection_params();
$query_args = array(
Expand All @@ -186,6 +188,14 @@ public function get_items( $request ) {
}
}

if ( $is_head_request ) {
// Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.
$query_args['fields'] = 'ids';
// Disable priming post meta for HEAD requests to improve performance.
$query_args['update_post_term_cache'] = false;
$query_args['update_post_meta_cache'] = false;
}

$revisions_query = new WP_Query();
$revisions = $revisions_query->query( $query_args );
$offset = isset( $query_args['offset'] ) ? (int) $query_args['offset'] : 0;
Expand Down Expand Up @@ -228,14 +238,18 @@ public function get_items( $request ) {
$page = (int) $request['page'];
}

$response = array();
if ( ! $is_head_request ) {
$response = array();

foreach ( $revisions as $revision ) {
$data = $this->prepare_item_for_response( $revision, $request );
$response[] = $this->prepare_response_for_collection( $data );
}
foreach ( $revisions as $revision ) {
$data = $this->prepare_item_for_response( $revision, $request );
$response[] = $this->prepare_response_for_collection( $data );
}

$response = rest_ensure_response( $response );
$response = rest_ensure_response( $response );
} else {
$response = new WP_REST_Response();
}

$response->header( 'X-WP-Total', (int) $total_revisions );
$response->header( 'X-WP-TotalPages', (int) $max_pages );
Expand Down Expand Up @@ -275,6 +289,11 @@ public function get_items( $request ) {
* @return WP_REST_Response|WP_Error Response object.
*/
public function prepare_item_for_response( $post, $request ) {
// Don't prepare the response body for HEAD requests.
if ( $request->is_method( 'HEAD' ) ) {
return new WP_REST_Response();
}

$parent = $this->get_parent( $request['parent'] );
$global_styles_config = $this->get_decoded_global_styles_json( $post->post_content );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ public function get_items( $request ) {
return $raw_patterns;
}

if ( $request->is_method( 'HEAD' ) ) {
// Return early as this handler doesn't add any response headers.
return new WP_REST_Response();
}

$response = array();

if ( $raw_patterns ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' );

Expand Down Expand Up @@ -178,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 @@ -402,6 +402,15 @@ static function ( $format ) {
// Force the post_type argument, since it's not a user input variable.
$args['post_type'] = $this->post_type;

$is_head_request = $request->is_method( 'HEAD' );
if ( $is_head_request ) {
// Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.
$args['fields'] = 'ids';
// Disable priming post meta for HEAD requests to improve performance.
$args['update_post_term_cache'] = false;
$args['update_post_meta_cache'] = false;
}

/**
* Filters WP_Query arguments when querying posts via the REST API.
*
Expand Down Expand Up @@ -434,22 +443,24 @@ static function ( $format ) {
add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 );
}

$posts = array();

update_post_author_caches( $query_result );
update_post_parent_caches( $query_result );
if ( ! $is_head_request ) {
$posts = array();

if ( post_type_supports( $this->post_type, 'thumbnail' ) ) {
update_post_thumbnail_cache( $posts_query );
}
update_post_author_caches( $query_result );
update_post_parent_caches( $query_result );

foreach ( $query_result as $post ) {
if ( ! $this->check_read_permission( $post ) ) {
continue;
if ( post_type_supports( $this->post_type, 'thumbnail' ) ) {
update_post_thumbnail_cache( $posts_query );
}

$data = $this->prepare_item_for_response( $post, $request );
$posts[] = $this->prepare_response_for_collection( $data );
foreach ( $query_result as $post ) {
if ( ! $this->check_read_permission( $post ) ) {
continue;
}

$data = $this->prepare_item_for_response( $post, $request );
$posts[] = $this->prepare_response_for_collection( $data );
}
}

// Reset filter.
Expand Down Expand Up @@ -479,7 +490,7 @@ static function ( $format ) {
);
}

$response = rest_ensure_response( $posts );
$response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $posts );

$response->header( 'X-WP-Total', (int) $total_posts );
$response->header( 'X-WP-TotalPages', (int) $max_pages );
Expand Down Expand Up @@ -1824,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
Loading
Loading