/**
  * Handles serving an API request.
  *
  * Matches the current server URI to a route and runs the first matching
  * callback then outputs a JSON representation of the returned value.
  *
  * @since 4.4.0
  * @access public
  *
  * @see WP_REST_Server::dispatch()
  *
  * @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used.
  *                     Default null.
  * @return false|null Null if not served and a HEAD request, false otherwise.
  */
 public function serve_request($path = null)
 {
     $content_type = isset($_GET['_jsonp']) ? 'application/javascript' : 'application/json';
     $this->send_header('Content-Type', $content_type . '; charset=' . get_option('blog_charset'));
     $this->send_header('X-Robots-Tag', 'noindex');
     $api_root = get_rest_url();
     if (!empty($api_root)) {
         $this->send_header('Link', '<' . esc_url_raw($api_root) . '>; rel="https://api.w.org/"');
     }
     /*
      * Mitigate possible JSONP Flash attacks.
      *
      * https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
      */
     $this->send_header('X-Content-Type-Options', 'nosniff');
     $this->send_header('Access-Control-Expose-Headers', 'X-WP-Total, X-WP-TotalPages');
     $this->send_header('Access-Control-Allow-Headers', 'Authorization');
     /**
      * Send nocache headers on authenticated requests.
      *
      * @since 4.4.0
      *
      * @param bool $rest_send_nocache_headers Whether to send no-cache headers.
      */
     $send_no_cache_headers = apply_filters('rest_send_nocache_headers', is_user_logged_in());
     if ($send_no_cache_headers) {
         foreach (wp_get_nocache_headers() as $header => $header_value) {
             $this->send_header($header, $header_value);
         }
     }
     /**
      * Filters whether the REST API is enabled.
      *
      * @since 4.4.0
      *
      * @param bool $rest_enabled Whether the REST API is enabled. Default true.
      */
     $enabled = apply_filters('rest_enabled', true);
     /**
      * Filters whether jsonp is enabled.
      *
      * @since 4.4.0
      *
      * @param bool $jsonp_enabled Whether jsonp is enabled. Default true.
      */
     $jsonp_enabled = apply_filters('rest_jsonp_enabled', true);
     $jsonp_callback = null;
     if (!$enabled) {
         echo $this->json_error('rest_disabled', __('The REST API is disabled on this site.'), 404);
         return false;
     }
     if (isset($_GET['_jsonp'])) {
         if (!$jsonp_enabled) {
             echo $this->json_error('rest_callback_disabled', __('JSONP support is disabled on this site.'), 400);
             return false;
         }
         $jsonp_callback = $_GET['_jsonp'];
         if (!wp_check_jsonp_callback($jsonp_callback)) {
             echo $this->json_error('rest_callback_invalid', __('The JSONP callback function is invalid.'), 400);
             return false;
         }
     }
     if (empty($path)) {
         if (isset($_SERVER['PATH_INFO'])) {
             $path = $_SERVER['PATH_INFO'];
         } else {
             $path = '/';
         }
     }
     $request = new WP_REST_Request($_SERVER['REQUEST_METHOD'], $path);
     $request->set_query_params(wp_unslash($_GET));
     $request->set_body_params(wp_unslash($_POST));
     $request->set_file_params($_FILES);
     $request->set_headers($this->get_headers(wp_unslash($_SERVER)));
     $request->set_body($this->get_raw_data());
     /*
      * HTTP method override for clients that can't use PUT/PATCH/DELETE. First, we check
      * $_GET['_method']. If that is not set, we check for the HTTP_X_HTTP_METHOD_OVERRIDE
      * header.
      */
     if (isset($_GET['_method'])) {
         $request->set_method($_GET['_method']);
     } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
         $request->set_method($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
     }
     $result = $this->check_authentication();
     if (!is_wp_error($result)) {
         $result = $this->dispatch($request);
     }
     // Normalize to either WP_Error or WP_REST_Response...
     $result = rest_ensure_response($result);
     // ...then convert WP_Error across.
     if (is_wp_error($result)) {
         $result = $this->error_to_response($result);
     }
     /**
      * Filters the API response.
      *
      * Allows modification of the response before returning.
      *
      * @since 4.4.0
      * @since 4.5.0 Applied to embedded responses.
      *
      * @param WP_HTTP_Response $result  Result to send to the client. Usually a WP_REST_Response.
      * @param WP_REST_Server   $this    Server instance.
      * @param WP_REST_Request  $request Request used to generate the response.
      */
     $result = apply_filters('rest_post_dispatch', rest_ensure_response($result), $this, $request);
     // Wrap the response in an envelope if asked for.
     if (isset($_GET['_envelope'])) {
         $result = $this->envelope_response($result, isset($_GET['_embed']));
     }
     // Send extra data from response objects.
     $headers = $result->get_headers();
     $this->send_headers($headers);
     $code = $result->get_status();
     $this->set_status($code);
     /**
      * Filters whether the request has already been served.
      *
      * Allow sending the request manually - by returning true, the API result
      * will not be sent to the client.
      *
      * @since 4.4.0
      *
      * @param bool             $served  Whether the request has already been served.
      *                                           Default false.
      * @param WP_HTTP_Response $result  Result to send to the client. Usually a WP_REST_Response.
      * @param WP_REST_Request  $request Request used to generate the response.
      * @param WP_REST_Server   $this    Server instance.
      */
     $served = apply_filters('rest_pre_serve_request', false, $result, $request, $this);
     if (!$served) {
         if ('HEAD' === $request->get_method()) {
             return null;
         }
         // Embed links inside the request.
         $result = $this->response_to_data($result, isset($_GET['_embed']));
         $result = wp_json_encode($result);
         $json_error_message = $this->get_json_last_error();
         if ($json_error_message) {
             $json_error_obj = new WP_Error('rest_encode_error', $json_error_message, array('status' => 500));
             $result = $this->error_to_response($json_error_obj);
             $result = wp_json_encode($result->data[0]);
         }
         if ($jsonp_callback) {
             // Prepend '/**/' to mitigate possible JSONP Flash attacks
             // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
             echo '/**/' . $jsonp_callback . '(' . $result . ')';
         } else {
             echo $result;
         }
     }
     return null;
 }
Пример #2
0
 /**
  * @dataProvider jsonp_callback_provider
  */
 public function test_jsonp_callback_check($callback, $valid)
 {
     $this->assertEquals($valid, wp_check_jsonp_callback($callback));
 }