Make WordPress Core


Ignore:
Timestamp:
03/02/2025 10:05:08 PM (3 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-sidebars-controller.php

    r58876 r59899  
    153153
    154154    /**
    155      * @ticket 41683
    156      */
    157     public function test_get_items_no_permission() {
     155     * @ticket 56481
     156     */
     157    public function test_get_items_with_head_request_should_not_prepare_sidebar_data() {
     158        wp_widgets_init();
     159
     160        $request = new WP_REST_Request( 'HEAD', '/wp/v2/sidebars' );
     161
     162        $hook_name = 'rest_prepare_sidebar';
     163        $filter    = new MockAction();
     164        $callback  = array( $filter, 'filter' );
     165
     166        add_filter( $hook_name, $callback );
     167        $response = rest_get_server()->dispatch( $request );
     168        remove_filter( $hook_name, $callback );
     169
     170        $this->assertNotWPError( $response );
     171
     172        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     173        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     174        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     175    }
     176
     177    /**
     178     * @dataProvider data_readable_http_methods
     179     * @ticket 41683
     180     * @ticket 56481
     181     *
     182     * @param string $method HTTP method to use.
     183     */
     184    public function test_get_items_no_permission( $method ) {
    158185        wp_set_current_user( 0 );
    159         $request  = new WP_REST_Request( 'GET', '/wp/v2/sidebars' );
     186        $request  = new WP_REST_Request( $method, '/wp/v2/sidebars' );
    160187        $response = rest_get_server()->dispatch( $request );
    161188        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
     
    502529
    503530    /**
    504      * @ticket 41683
    505      */
    506     public function test_get_item_no_permission() {
     531     * @dataProvider data_readable_http_methods
     532     * @ticket 56481
     533     *
     534     * @param string $method The HTTP method to use.
     535     */
     536    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     537        $hook_name = 'rest_prepare_sidebar';
     538        $filter    = new MockAction();
     539        $callback  = array( $filter, 'filter' );
     540        add_filter( $hook_name, $callback );
     541        $header_filter = new class() {
     542            public static function add_custom_header( $response ) {
     543                $response->header( 'X-Test-Header', 'Test' );
     544
     545                return $response;
     546            }
     547        };
     548        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     549
     550        $this->setup_sidebar(
     551            'sidebar-1',
     552            array(
     553                'name' => 'Test sidebar',
     554            )
     555        );
     556
     557        $request  = new WP_REST_Request( $method, '/wp/v2/sidebars/sidebar-1' );
     558        $response = rest_get_server()->dispatch( $request );
     559        remove_filter( $hook_name, $callback );
     560        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     561
     562        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     563        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     564        $headers = $response->get_headers();
     565        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     566        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     567        if ( 'HEAD' !== $method ) {
     568            return null;
     569        }
     570        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     571    }
     572
     573    /**
     574     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     575     *
     576     * @return array
     577     */
     578    public static function data_readable_http_methods() {
     579        return array(
     580            'GET request'  => array( 'GET' ),
     581            'HEAD request' => array( 'HEAD' ),
     582        );
     583    }
     584
     585    /**
     586     * @dataProvider data_readable_http_methods
     587     * @ticket 41683
     588     * @ticket 56481
     589     *
     590     * @param string $method The HTTP method to use.
     591     */
     592    public function test_get_item_no_permission( $method ) {
    507593        wp_set_current_user( 0 );
    508594        $this->setup_sidebar(
     
    513599        );
    514600
    515         $request  = new WP_REST_Request( 'GET', '/wp/v2/sidebars/sidebar-1' );
     601        $request  = new WP_REST_Request( $method, '/wp/v2/sidebars/sidebar-1' );
    516602        $response = rest_get_server()->dispatch( $request );
    517603        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
     
    553639
    554640    /**
    555      * @ticket 41683
    556      */
    557     public function test_get_item_wrong_permission_author() {
     641     * @dataProvider data_readable_http_methods
     642     * @ticket 41683
     643     * @ticket 56481
     644     *
     645     * @param string $method The HTTP method to use.
     646     */
     647    public function test_get_item_wrong_permission_author( $method ) {
    558648        wp_set_current_user( self::$author_id );
    559649        $this->setup_sidebar(
     
    564654        );
    565655
    566         $request  = new WP_REST_Request( 'GET', '/wp/v2/sidebars/sidebar-1' );
     656        $request  = new WP_REST_Request( $method, '/wp/v2/sidebars/sidebar-1' );
    567657        $response = rest_get_server()->dispatch( $request );
    568658        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 403 );
Note: See TracChangeset for help on using the changeset viewer.