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-widget-types-controller.php

    r56559 r59899  
    123123
    124124    /**
     125     * @ticket 56481
     126     */
     127    public function test_get_items_with_head_request_should_not_prepare_widget_types_data() {
     128        wp_set_current_user( self::$admin_id );
     129        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/widget-types' );
     130        $response = rest_get_server()->dispatch( $request );
     131        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     132        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     133    }
     134
     135    /**
    125136     * @ticket 53303
    126137     */
     
    183194
    184195    /**
     196     * @dataProvider data_readable_http_methods
     197     * @ticket 56481
     198     *
     199     * @param string $method The HTTP method to use.
     200     */
     201    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     202        $widget_name = 'calendar';
     203        wp_set_current_user( self::$admin_id );
     204        $request = new WP_REST_Request( $method, '/wp/v2/widget-types/' . $widget_name );
     205
     206        $hook_name = 'rest_prepare_widget_type';
     207        $filter    = new MockAction();
     208        $callback  = array( $filter, 'filter' );
     209        add_filter( $hook_name, $callback );
     210        $header_filter = new class() {
     211            public static function add_custom_header( $response ) {
     212                $response->header( 'X-Test-Header', 'Test' );
     213
     214                return $response;
     215            }
     216        };
     217        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     218        $response = rest_get_server()->dispatch( $request );
     219        remove_filter( $hook_name, $callback );
     220        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     221
     222        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     223        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     224        $headers = $response->get_headers();
     225        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     226        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     227        if ( 'HEAD' !== $method ) {
     228            return null;
     229        }
     230        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     231    }
     232
     233    /**
     234     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     235     *
     236     * @return array
     237     */
     238    public static function data_readable_http_methods() {
     239        return array(
     240            'GET request'  => array( 'GET' ),
     241            'HEAD request' => array( 'HEAD' ),
     242        );
     243    }
     244
     245    /**
    185246     * @ticket 41683
    186247     */
     
    201262
    202263    /**
    203      * @ticket 41683
    204      */
    205     public function test_get_widget_invalid_name() {
     264     * @dataProvider data_readable_http_methods
     265     * @ticket 41683
     266     * @ticket 56481
     267     *
     268     * @param string $method HTTP method to use.
     269     */
     270    public function test_get_widget_invalid_name( $method ) {
    206271        $widget_type = 'fake';
    207272        wp_set_current_user( self::$admin_id );
    208         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types/' . $widget_type );
     273        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types/' . $widget_type );
    209274        $response = rest_get_server()->dispatch( $request );
    210275
     
    252317
    253318    /**
    254      * @ticket 41683
    255      */
    256     public function test_get_items_wrong_permission() {
     319     * @dataProvider data_readable_http_methods
     320     * @ticket 41683
     321     * @ticket 56481
     322     *
     323     * @param string $method HTTP method to use.
     324     */
     325    public function test_get_items_wrong_permission( $method ) {
    257326        wp_set_current_user( self::$subscriber_id );
    258         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types' );
     327        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types' );
    259328        $response = rest_get_server()->dispatch( $request );
    260329        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 403 );
     
    262331
    263332    /**
    264      * @ticket 41683
    265      */
    266     public function test_get_item_wrong_permission() {
     333     * @dataProvider data_readable_http_methods
     334     * @ticket 41683
     335     * @ticket 56481
     336     *
     337     * @param string $method HTTP method to use.
     338     */
     339    public function test_get_item_wrong_permission( $method ) {
    267340        wp_set_current_user( self::$subscriber_id );
    268         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types/calendar' );
     341        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types/calendar' );
    269342        $response = rest_get_server()->dispatch( $request );
    270343        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 403 );
     
    272345
    273346    /**
    274      * @ticket 41683
    275      */
    276     public function test_get_items_no_permission() {
     347     * @dataProvider data_readable_http_methods
     348     * @ticket 41683
     349     * @ticket 56481
     350     *
     351     * @param string $method HTTP method to use.
     352     */
     353    public function test_get_items_no_permission( $method ) {
    277354        wp_set_current_user( 0 );
    278         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types' );
     355        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types' );
    279356        $response = rest_get_server()->dispatch( $request );
    280357        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
     
    282359
    283360    /**
    284      * @ticket 41683
    285      */
    286     public function test_get_item_no_permission() {
     361     * @dataProvider data_readable_http_methods
     362     * @ticket 41683
     363     * @ticket 56481
     364     *
     365     * @param string $method HTTP method to use.
     366     */
     367    public function test_get_item_no_permission( $method ) {
    287368        wp_set_current_user( 0 );
    288         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types/calendar' );
     369        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types/calendar' );
    289370        $response = rest_get_server()->dispatch( $request );
    290371        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
Note: See TracChangeset for help on using the changeset viewer.