diff --git a/plugins/rest-api.php b/plugins/rest-api.php new file mode 100644 index 00000000..89cd128f --- /dev/null +++ b/plugins/rest-api.php @@ -0,0 +1,436 @@ + 'GET', + 'callback' => 'test_foo', + ) ); +} ); + +function test_foo( $request ) { + sleep( 2 ); // this helps make it obvious whether a response is cached or not + + $bar = $request->get_param( 'bar' ); + + return array( + 'bar' => $bar, + 'time' => time(), + ); +} + + * Then make a request to that endpoint. The response body will contain the timestamp, and the value of the `bar` + * parameter (or `null` if you don't pass one). The headers will reveal how the response was generated and served. + * + * curl -i http://example.localhost/wp-json/test/v1/foo + * + * Uncached responses will contain: Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages + * Cached responses served via PHP will contain: WP-Super-Cache: Served supercache file from PHP + * Cached responses served via Apache will contain: Last-Modified: Tue, 23 Aug 2016 18:12:48 GMT + * + * The headers can vary a bit depending on the environment, but you should see some consistent differences between + * cached and uncached requests. + * + * You can also check the following signals: + * + * - Has the value of `time` in the response body changed? + * - If you manually edit the endpoint's .json file in `wp-content/cache`, do you see the change? + * - Was the response slow (2+ seconds), or fast? + * - Do you get a fresh response if you delete the endpoint's .json file in `wp-content/cache`? + */ + + +/* + * Future development: + * + * See if there's a way to serve gzipped json while still sending the correct content-type header + * can use Apache's ModMime? + * Add filter to $gziprules in wpsc_get_htaccess_info + * Add `` to cache/.htaccess + * Add `RewriteCond %{HTTP:Accept-Encoding} gzip` rules to /.htaccess + * maybe detect if server is configured to send application/json for gzipped + * if it's not, then don't generate the corresponding rules? + * could maybe put AddType into htaccess to set gzip mime type + * thought not all servers supported, but w3 total cache does it. maybe they detect support first? + * + * When disabling the plugin, clear all cached files to get rid of the API cache, then run the preloader to regenerate everything else (if it's enabled) + * + * Need to update the cache when underlying data changes, rather than just waiting for manual expiration? + * would be bad if deleted post continue to show up in cache, etc + * how to tell that updating a post or category maps to an API endpoint? + * worst case, could just flush all endpoint cache files when any post/comment/taxterm/etc is updated + * + * JSON spec doesn't allow comments, but still want the helpful info from $wp_super_cache_comments + * so instead, maybe send X-WPSC-REST headers with that info + * maybe can't get that b/c has to be set in phase2, but can at least output whether loading from cache or not. already has a header for that, though + * could store in meta files, like headers + * + * Add support for preloading + * + * Add .json tests to the Cache Tester button on Settings > Easy ? + * maybe send a test json response to make sure the header is getting set correctly + * if not, tell user to ask host to configure apache to send application/json for .json files + * + * Run `wpsc_rest_save_url_prefix` once during WPSC setup, and then on a cron after that + * that's cleaner/faster than loading on every request + */ + +/** + * Load the plugin + */ +function wpsc_rest_bootstrap() { + global $cache_rest_api; + + // Always register the plugin settings + add_cacheaction( 'cache_admin_page', 'wpsc_rest_render_plugin_settings' ); + + /* + * Disable the rest of the plugin if that was explicitly configured. + * Otherwise, leave it enabled by default and set a canonical value for `$cache_rest_api`. + */ + if ( isset( $cache_rest_api ) && '0' === $cache_rest_api ) { + return; + } else { + $cache_rest_api = '1'; + } + + add_cacheaction( 'add_cacheaction', 'wpsc_rest_bootstrap_phase2' ); + add_cacheaction( 'supercache_filename', 'wpsc_rest_json_filenames' ); + add_cacheaction( 'serve_supercache_file', 'wpsc_rest_serve_supercache_file' ); + add_cacheaction( 'before_serve_supercache_file', 'wpsc_send_json_headers' ); +} + +/** + * Register hook callbacks for phase 2 + * + * We have to wait until phase 2 to register these, since WP isn't fully loaded + * during phase 1. + */ +function wpsc_rest_bootstrap_phase2() { + global $cache_enabled; + + add_filter( 'wp_cache_eof_tags', 'wpsc_rest_eof_tags' ); + add_filter( 'wp_cache_ob_callback_filter', 'wpsc_rest_remove_comments' ); + add_filter( 'supercache_filename', 'wpsc_rest_json_filenames' ); + add_filter( 'supercacherewriterules', 'wpsc_rest_add_rewrite_rules', 10, 4 ); + + // Only hook into Core if caching is enabled, to avoid unintended side-effects + if ( $cache_enabled ) { + add_action( 'init', 'wpsc_rest_save_url_prefix' ); + } +} + +/** + * Render the REST API section of the Plugins tab + */ +function wpsc_rest_render_plugin_settings() { + global $cache_rest_api; + + $results = wpsc_process_plugin_settings(); + + ?> + +
+

+ +
+ + + + + + +

+ WordPress\' REST API.', 'wp-super-cache' ); ?> +

+ + +
+ %s.', 'wp-super-cache' ), $results['status'] ); ?> +
+ + +
+ + /> +
+
+
+ + ', $json_rules . '', $rules ); +} + +/** + * Modify the condition rules for use with JSON rewrite rules + * + * If the permalink structure has a trailing slash, then the condition rules generated by + * `wpsc_get_condition_rules()` will include rules that are designed to prevent URLs without a slash from + * matching. That is done because Core sends a `301` status and redirects unslashed URLs to the canonical, + * slashed equivalent. + * + * Core doesn't do that for the REST API, though; unslashed URLs are treated identically to slashed URLs, so + * there's no need to preventing matching. By removing those rules, the unslashed URLs become cacheable. + * + * @return string + */ +function wpsc_rest_get_condition_rules() { + $condition_rules = preg_grep( + '/RewriteCond %{REQUEST_URI} \!\^\.\*/', + wpsc_get_condition_rules(), + PREG_GREP_INVERT + ); + + return implode( "\n", $condition_rules ); +} + +wpsc_rest_bootstrap(); diff --git a/readme.txt b/readme.txt index fbfb6416..dce6fbbb 100644 --- a/readme.txt +++ b/readme.txt @@ -466,6 +466,12 @@ You'll have to use a separate mobile plugin to render a page formatted for those * [WordPress Mobile Edition](http://wordpress.org/plugins/wordpress-mobile-edition/) * [WordPress Mobile Pack](http://wordpress.org/plugins/wordpress-mobile-pack/) (can't have "Don't cache pages for known users." enabled) += Why aren't REST API endpoints being cached? = + +If you're using mod_rewrite, it's possible that your .htaccess file needs to be regenerated. +To find out, open the Advanced Settings page, and look at Mod Rewrite Rules section. +If you see a message telling you that a difference has been detected, then click on the `Upgrade Mod_Rewrite Rules` button. + = Troubleshooting = If things don't work when you installed the plugin here are a few things to check: diff --git a/wp-cache.php b/wp-cache.php index c9872d6a..d2efe717 100644 --- a/wp-cache.php +++ b/wp-cache.php @@ -3120,23 +3120,27 @@ function wpsc_get_htaccess_info() { $rules .= "AddDefaultCharset {$charset}\n"; } + $rules .= "# HTML / HTTPS / gzipped\n"; $rules .= "CONDITION_RULES"; $rules .= "RewriteCond %{HTTP:Accept-Encoding} gzip\n"; $rules .= "RewriteCond %{HTTPS} on\n"; $rules .= "RewriteCond {$apache_root}{$inst_root}cache/supercache/%{SERVER_NAME}{$home_root_lc}$1/index-https.html.gz -f\n"; $rules .= "RewriteRule ^(.*) \"{$inst_root}cache/supercache/%{SERVER_NAME}{$home_root_lc}$1/index-https.html.gz\" [L]\n\n"; + $rules .= "# HTML / HTTP / gzipped\n"; $rules .= "CONDITION_RULES"; $rules .= "RewriteCond %{HTTP:Accept-Encoding} gzip\n"; $rules .= "RewriteCond %{HTTPS} !on\n"; $rules .= "RewriteCond {$apache_root}{$inst_root}cache/supercache/%{SERVER_NAME}{$home_root_lc}$1/index.html.gz -f\n"; $rules .= "RewriteRule ^(.*) \"{$inst_root}cache/supercache/%{SERVER_NAME}{$home_root_lc}$1/index.html.gz\" [L]\n\n"; + $rules .= "# HTML / HTTPS / uncompressed\n"; $rules .= "CONDITION_RULES"; $rules .= "RewriteCond %{HTTPS} on\n"; $rules .= "RewriteCond {$apache_root}{$inst_root}cache/supercache/%{SERVER_NAME}{$home_root_lc}$1/index-https.html -f\n"; $rules .= "RewriteRule ^(.*) \"{$inst_root}cache/supercache/%{SERVER_NAME}{$home_root_lc}$1/index-https.html\" [L]\n\n"; + $rules .= "# HTML / HTTP / uncompressed\n"; $rules .= "CONDITION_RULES"; $rules .= "RewriteCond %{HTTPS} !on\n"; $rules .= "RewriteCond {$apache_root}{$inst_root}cache/supercache/%{SERVER_NAME}{$home_root_lc}$1/index.html -f\n";