Make WordPress Core


Ignore:
Timestamp:
03/02/2025 10:05:08 PM (17 months ago)
Author:
TimothyBlynJacobs
Message:

REST API: Improve performance for HEAD requests.

By default, the REST API responds to HEAD rqeuests by calling the GET handler and omitting the body from the response. While convenient, this ends up performing needless work that slows down the API response time.

This commit adjusts the Core controllers to specifically handle HEAD requests by not preparing the response body.

Fixes #56481.
Props antonvlasenko, janusdev, ironprogrammer, swissspidy, spacedmonkey, mukesh27, mamaduka, timothyblynjacobs.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/rest-api/rest-autosaves-controller.php

    r56745 r59899  
    180180        }
    181181
    182         public function test_get_items_no_permission() {
     182        /**
     183         * @ticket 56481
     184         */
     185        public function test_get_items_with_head_request_should_not_prepare_autosaves_data() {
     186                $request = new WP_REST_Request( 'HEAD', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     187
     188                $hook_name = 'rest_prepare_autosave';
     189                $filter    = new MockAction();
     190                $callback  = array( $filter, 'filter' );
     191
     192                add_filter( $hook_name, $callback );
     193                $response = rest_get_server()->dispatch( $request );
     194                remove_filter( $hook_name, $callback );
     195
     196                $this->assertNotWPError( $response );
     197                $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     198                $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     199                $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     200        }
     201
     202        /**
     203         * @dataProvider data_readable_http_methods
     204         * @ticket 56481
     205         *
     206         * @param string $method The HTTP method to use.
     207         */
     208        public function test_get_items_no_permission( $method ) {
    183209                wp_set_current_user( 0 );
    184                 $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     210                $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/autosaves' );
    185211                $response = rest_get_server()->dispatch( $request );
    186212                $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
     
    190216        }
    191217
    192         public function test_get_items_missing_parent() {
    193                 wp_set_current_user( self::$editor_id );
    194                 $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves' );
     218        /**
     219         * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     220         *
     221         * @return array
     222         */
     223        public static function data_readable_http_methods() {
     224                return array(
     225                        'GET request'  => array( 'GET' ),
     226                        'HEAD request' => array( 'HEAD' ),
     227                );
     228        }
     229
     230        /**
     231         * @dataProvider data_readable_http_methods
     232         * @ticket 56481
     233         *
     234         * @param string $method The HTTP method to use.
     235         */
     236        public function test_get_items_missing_parent( $method ) {
     237                wp_set_current_user( self::$editor_id );
     238                $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves' );
    195239                $response = rest_get_server()->dispatch( $request );
    196240                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
    197241        }
    198242
    199         public function test_get_items_invalid_parent_post_type() {
    200                 wp_set_current_user( self::$editor_id );
    201                 $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     243        /**
     244         * @dataProvider data_readable_http_methods
     245         * @ticket 56481
     246         *
     247         * @param string $method The HTTP method to use.
     248         */
     249        public function test_get_items_invalid_parent_post_type( $method ) {
     250                wp_set_current_user( self::$editor_id );
     251                $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$page_id . '/autosaves' );
    202252                $response = rest_get_server()->dispatch( $request );
    203253                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     
    231281        }
    232282
     283        /**
     284         * @dataProvider data_readable_http_methods
     285         * @ticket 56481
     286         *
     287         * @param string $method The HTTP method to use.
     288         */
     289        public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     290                wp_set_current_user( self::$editor_id );
     291                $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     292
     293                $hook_name = 'rest_prepare_autosave';
     294                $filter    = new MockAction();
     295                $callback  = array( $filter, 'filter' );
     296                add_filter( $hook_name, $callback );
     297                $header_filter = new class() {
     298                        public static function add_custom_header( $response ) {
     299                                $response->header( 'X-Test-Header', 'Test' );
     300
     301                                return $response;
     302                        }
     303                };
     304                add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     305                $response = rest_get_server()->dispatch( $request );
     306                remove_filter( $hook_name, $callback );
     307                remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     308
     309                $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     310                $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     311                $headers = $response->get_headers();
     312                $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     313                $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     314                if ( 'HEAD' !== $method ) {
     315                        return null;
     316                }
     317                $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     318        }
     319
    233320        public function test_get_item_embed_context() {
    234321                wp_set_current_user( self::$editor_id );
     
    249336        }
    250337
    251         public function test_get_item_no_permission() {
    252                 $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     338        /**
     339         * @dataProvider data_readable_http_methods
     340         * @ticket 56481
     341         *
     342         * @param string $method The HTTP method to use.
     343         */
     344        public function test_get_item_no_permission( $method ) {
     345                $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
    253346                wp_set_current_user( self::$contributor_id );
    254347                $response = rest_get_server()->dispatch( $request );
     
    256349        }
    257350
    258         public function test_get_item_missing_parent() {
    259                 wp_set_current_user( self::$editor_id );
    260                 $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves/' . self::$autosave_post_id );
     351        /**
     352         * @dataProvider data_readable_http_methods
     353         * @ticket 56481
     354         *
     355         * @param string $method The HTTP method to use.
     356         */
     357        public function test_get_item_missing_parent( $method ) {
     358                wp_set_current_user( self::$editor_id );
     359                $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves/' . self::$autosave_post_id );
    261360                $response = rest_get_server()->dispatch( $request );
    262361                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
    263362        }
    264363
    265         public function test_get_item_invalid_parent_post_type() {
    266                 wp_set_current_user( self::$editor_id );
    267                 $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     364        /**
     365         * @dataProvider data_readable_http_methods
     366         * @ticket 56481
     367         *
     368         * @param string $method The HTTP method to use.
     369         */
     370        public function test_get_item_invalid_parent_post_type( $method ) {
     371                wp_set_current_user( self::$editor_id );
     372                $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$page_id . '/autosaves' );
    268373                $response = rest_get_server()->dispatch( $request );
    269374                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
Note: See TracChangeset for help on using the changeset viewer.