-
-
Save westonruter/48e546e4636bec5fafd8a29e0fc7a001 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* AMP Debug Validation Request plugin initialization file. | |
* | |
* @package AMP_Debug_Validation_Request | |
* @author Weston Ruter, Google | |
* @license GPL-2.0-or-later | |
* @copyright 2019 Google Inc. | |
* | |
* @wordpress-plugin | |
* Plugin Name: AMP Debug Validation Request | |
* Plugin URI: https://gist.github.com/westonruter/48e546e4636bec5fafd8a29e0fc7a001 | |
* Description: Debug validation request failures. | |
* Version: 0.3.0 | |
* Author: Weston Ruter, Google | |
* Author URI: https://weston.ruter.net/ | |
* License: GNU General Public License v2 (or later) | |
* License URI: http://www.gnu.org/licenses/gpl-2.0.html | |
* Gist Plugin URI: https://gist.github.com/westonruter/48e546e4636bec5fafd8a29e0fc7a001 | |
*/ | |
namespace AMP_Debug_Validation_Request; | |
use AMP_Validation_Manager; | |
use WP_Admin_Bar; | |
use WP_Error; | |
const QUERY_VAR = 'amp_debug_validate_request'; | |
add_action( | |
'parse_query', | |
function () { | |
if ( ! isset( $_GET[ QUERY_VAR ] ) || ! class_exists( 'AMP_Validation_Manager' ) ) { | |
return; | |
} | |
if ( ! AMP_Validation_Manager::has_cap() ) { | |
wp_die( 'Please log in before doing this.' ); | |
} | |
if ( ! wp_verify_nonce( wp_unslash( $_GET[ QUERY_VAR ] ), QUERY_VAR ) ) { | |
wp_die( 'Unable to verify nonce.' ); | |
} | |
$current_url = amp_get_current_url(); | |
$current_url = remove_query_arg( QUERY_VAR, $current_url ); | |
$results = validate_url( $current_url ); | |
$data = [ | |
'success' => ! is_wp_error( $results ), | |
'results' => $results, | |
]; | |
header( 'Content-Type: application/json' ); | |
echo wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); | |
exit; | |
} | |
); | |
add_action( | |
'admin_bar_menu', | |
function ( WP_Admin_Bar $admin_bar ) { | |
if ( ! $admin_bar->get_node( 'amp' ) || ! AMP_Validation_Manager::has_cap() || ! class_exists( 'AMP_Validation_Manager' ) ) { | |
return; | |
} | |
$admin_bar->add_node( | |
[ | |
'id' => 'amp-debug-validation-response-directly', | |
'title' => 'Debug AMP validation response directly', | |
'href' => add_query_arg( AMP_Validation_Manager::VALIDATE_QUERY_VAR, AMP_Validation_Manager::get_amp_validate_nonce() ), | |
'parent' => 'amp', | |
'meta' => [ | |
'target' => '_blank', | |
], | |
] | |
); | |
$admin_bar->add_node( | |
[ | |
'id' => 'amp-debug-validation-response-with-loopback-request', | |
'title' => 'Debug AMP validation response w/ loopback request', | |
'href' => add_query_arg( QUERY_VAR, wp_create_nonce( QUERY_VAR ) ), | |
'parent' => 'amp', | |
'meta' => [ | |
'target' => '_blank', | |
], | |
] | |
); | |
}, | |
1000 | |
); | |
// Work around issue where amp_validate is included in url key of response. | |
add_action( | |
'wp', | |
function () { | |
if ( ! class_exists( 'AMP_Validation_Manager' ) ) { | |
return; | |
} | |
$_SERVER['REQUEST_URI'] = remove_query_arg( AMP_Validation_Manager::VALIDATE_QUERY_VAR, $_SERVER['REQUEST_URI'] ); | |
} | |
); | |
/** | |
* Validate URL. | |
* | |
* Patched version of `AMP_Validation_Manager::validate_url()`. | |
* | |
* @param string $url URL. | |
* @return array|WP_Error | |
*/ | |
function validate_url( $url ) { | |
$added_query_vars = [ | |
AMP_Validation_Manager::VALIDATE_QUERY_VAR => AMP_Validation_Manager::get_amp_validate_nonce(), | |
AMP_Validation_Manager::CACHE_BUST_QUERY_VAR => wp_rand(), | |
]; | |
$validation_url = add_query_arg( $added_query_vars, $url ); | |
$r = null; | |
/** This filter is documented in wp-includes/class-http.php */ | |
$allowed_redirects = apply_filters( 'http_request_redirection_count', 5 ); | |
for ( $redirect_count = 0; $redirect_count < $allowed_redirects; $redirect_count++ ) { | |
$r = wp_remote_get( | |
$validation_url, | |
[ | |
'cookies' => wp_unslash( $_COOKIE ), // Pass along cookies so private pages and drafts can be accessed. | |
'timeout' => 15, // Increase from default of 5 to give extra time for the plugin to identify the sources for any given validation errors. | |
'sslverify' => false, | |
'redirection' => 0, // Because we're in a loop for redirection. | |
'headers' => [ | |
'Cache-Control' => 'no-cache', | |
], | |
] | |
); | |
// If the response is not a redirect, then break since $r is all we need. | |
$response_code = wp_remote_retrieve_response_code( $r ); | |
$location_header = wp_remote_retrieve_header( $r, 'Location' ); | |
$is_redirect = ( | |
$response_code | |
&& | |
$response_code > 300 && $response_code < 400 | |
&& | |
$location_header | |
); | |
if ( ! $is_redirect ) { | |
break; | |
} | |
// Ensure absolute URL. | |
if ( '/' === substr( $location_header, 0, 1 ) ) { | |
$location_header = preg_replace( '#(^https?://[^/]+)/.*#', '$1', home_url( '/' ) ) . $location_header; | |
} | |
// Block redirecting to a different host. | |
$location_header = wp_validate_redirect( $location_header ); | |
if ( ! $location_header ) { | |
break; | |
} | |
$validation_url = add_query_arg( $added_query_vars, $location_header ); | |
} | |
if ( is_wp_error( $r ) ) { | |
return $r; | |
} | |
$response = trim( wp_remote_retrieve_body( $r ) ); | |
if ( wp_remote_retrieve_response_code( $r ) >= 400 ) { | |
$data = json_decode( $response, true ); | |
return new WP_Error( | |
is_array( $data ) && isset( $data['code'] ) ? $data['code'] : wp_remote_retrieve_response_code( $r ), | |
is_array( $data ) && isset( $data['message'] ) ? $data['message'] : wp_remote_retrieve_response_message( $r ) | |
); | |
} | |
if ( wp_remote_retrieve_response_code( $r ) >= 300 ) { | |
return new WP_Error( | |
'http_request_failed', | |
__( 'Too many redirects.', 'amp' ) | |
); | |
} | |
$url = remove_query_arg( | |
array_keys( $added_query_vars ), | |
$validation_url | |
); | |
// Strip byte order mark (BOM). | |
while ( "\xEF\xBB\xBF" === substr( $response, 0, 3 ) ) { | |
$response = substr( $response, 3 ); | |
} | |
// Strip any leading whitespace. | |
$response = ltrim( $response ); | |
// Strip HTML comments that may have been injected at the end of the response (e.g. by a caching plugin). | |
$response = preg_replace( '/<!--.*?-->\s*$/s', '', $response ); | |
$data = [ | |
'first_kb_response' => substr( $response, 0, 1024 ), | |
]; | |
if ( '' === $response ) { | |
return new WP_Error( 'white_screen_of_death', '', $data ); | |
} | |
if ( '{' !== substr( $response, 0, 1 ) ) { | |
return new WP_Error( 'response_not_json', '', $data ); | |
} | |
$validation = json_decode( $response, true ); | |
if ( json_last_error() || ! isset( $validation['results'] ) || ! is_array( $validation['results'] ) ) { | |
return new WP_Error( 'malformed_json_validation_errors', '', $data ); | |
} | |
return array_merge( | |
$validation, | |
compact( 'url' ) | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Installation instructions: https://gist.github.com/westonruter/6110fbc4bef0c4b8c021a112012f7e9c