Skip to content

Commit

Permalink
Use query vars instead of URL for computing slug; add HMAC
Browse files Browse the repository at this point in the history
  • Loading branch information
westonruter committed Nov 10, 2023
1 parent dd9b375 commit f9a1493
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 25 deletions.
5 changes: 5 additions & 0 deletions modules/images/image-loading-optimization/detection.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,17 @@ function ilo_print_detection_script() {
*/
$detection_time_window = apply_filters( 'perflab_image_loading_detection_time_window', 5000 );

$query_vars = ilo_get_normalized_query_vars();
$slug = ilo_get_page_metrics_slug( $query_vars );

$detect_args = array(
'serveTime' => $serve_time,
'detectionTimeWindow' => $detection_time_window,
'isDebug' => WP_DEBUG,
'restApiEndpoint' => rest_url( ILO_REST_API_NAMESPACE . ILO_PAGE_METRICS_ROUTE ),
'restApiNonce' => wp_create_nonce( 'wp_rest' ),
'pageMetricsSlug' => $slug,
'pageMetricsHmac' => ilo_get_slug_hmac( $slug ), // TODO: Or would a nonce make more sense with the $slug being the action?
);
wp_print_inline_script_tag(
sprintf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,17 @@ function getBreadcrumbs( leafElement ) {
* @param {boolean} args.isDebug Whether to show debug messages.
* @param {string} args.restApiEndpoint URL for where to send the detection data.
* @param {string} args.restApiNonce Nonce for writing to the REST API.
* @param {string} args.pageMetricsSlug Slug for page metrics.
* @param {string} args.pageMetricsHmac HMAC for the page metric slug.
*/
export default async function detect( {
serveTime,
detectionTimeWindow,
isDebug,
restApiEndpoint,
restApiNonce,
pageMetricsSlug,
pageMetricsHmac,
} ) {
const runTime = new Date().valueOf();

Expand Down Expand Up @@ -259,7 +263,9 @@ export default async function detect( {

/** @type {PageMetrics} */
const pageMetrics = {
url: win.location.href, // TODO: Consider sending canonical URL instead.
url: win.location.href,
slug: pageMetricsSlug,
hmac: pageMetricsHmac,
viewport: {
width: win.innerWidth,
height: win.innerHeight,
Expand Down
24 changes: 24 additions & 0 deletions modules/images/image-loading-optimization/storage/data.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,30 @@ function ilo_get_normalized_query_vars() {
return $normalized_query_vars;
}

/**
* Gets slug for page metrics.
*
* @see ilo_get_normalized_query_vars()
*
* @param array $query_vars Normalized query vars.
* @return string Slug.
*/
function ilo_get_page_metrics_slug( $query_vars ) {
return md5( wp_json_encode( $query_vars ) );
}

/**
* Compute HMAC for page metrics slug.
*
* This is used in the REST API to authenticate the storage of new page metrics from a given URL.
*
* @param string $slug Page metrics slug.
* @return false HMAC.
*/
function ilo_get_slug_hmac( $slug ) {
return hash_hmac( 'sha1', $slug, wp_salt() );
}

/**
* Unshift a new page metric onto an array of page metrics.
*
Expand Down
32 changes: 10 additions & 22 deletions modules/images/image-loading-optimization/storage/post-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,18 @@ function ilo_register_page_metrics_post_type() {
}
add_action( 'init', 'ilo_register_page_metrics_post_type' );

/**
* Gets slug for page metrics post.
*
* @param string $url URL.
* @return string Slug for URL.
*/
function ilo_get_page_metrics_slug( $url ) {
return md5( $url );
}

/**
* Get page metrics post.
*
* @param string $url URL.
* @param string $slug Page metrics slug.
* @return WP_Post|null Post object if exists.
*/
function ilo_get_page_metrics_post( $url ) {
function ilo_get_page_metrics_post( $slug ) {
$post_query = new WP_Query(
array(
'post_type' => ILO_PAGE_METRICS_POST_TYPE,
'post_status' => 'publish',
'name' => ilo_get_page_metrics_slug( $url ),
'name' => $slug,
'posts_per_page' => 1,
'no_found_rows' => true,
'cache_results' => true,
Expand Down Expand Up @@ -114,29 +104,27 @@ function ilo_parse_stored_page_metrics( WP_Post $post ) {
* The $validated_page_metric parameter has the following array shape:
*
* {
* 'url': string,
* 'viewport': array{
* 'width': int,
* 'height': int
* },
* 'elements': array
* }
*
* @param array $validated_page_metric Page metric, already validated by REST API.
*
* @param string $url URL for the page metrics. This is used purely as metadata.
* @param string $slug Page metrics slug (computed from query vars).
* @param array $validated_page_metric Page metric, already validated by REST API.
* @return int|WP_Error Post ID or WP_Error otherwise.
*/
function ilo_store_page_metric( array $validated_page_metric ) {
$url = $validated_page_metric['url'];
unset( $validated_page_metric['url'] ); // Not stored in post_content but rather in post_title/post_name.
function ilo_store_page_metric( $url, $slug, array $validated_page_metric ) {
$validated_page_metric['timestamp'] = time();

// TODO: What about storing a version identifier?
$post_data = array(
'post_title' => $url,
'post_title' => $url, // TODO: Should we keep this? It can help with debugging.
);

$post = ilo_get_page_metrics_post( $url );
$post = ilo_get_page_metrics_post( $slug );

if ( $post instanceof WP_Post ) {
$post_data['ID'] = $post->ID;
Expand All @@ -150,7 +138,7 @@ function ilo_store_page_metric( array $validated_page_metric ) {
$page_metrics = array();
}
} else {
$post_data['post_name'] = ilo_get_page_metrics_slug( $url );
$post_data['post_name'] = $slug;
$page_metrics = array();
}

Expand Down
20 changes: 18 additions & 2 deletions modules/images/image-loading-optimization/storage/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ function ilo_register_endpoint() {
return true;
},
),
'slug' => array(
'type' => 'string',
'required' => true,
'pattern' => '^[0-9a-f]{32}$',
),
'hmac' => array(
'type' => 'string',
'required' => true,
'pattern' => '^[0-9a-f]+$',
'validate_callback' => static function ( $hmac, WP_REST_Request $request ) {
if ( ! hash_equals( $hmac, ilo_get_slug_hmac( $request->get_param( 'slug' ) ) ) ) {
return new WP_Error( 'invalid_hmac', __( 'HMAC comparison failure.', 'performance-lab' ) );
}
return true;
},
),
'viewport' => array(
'description' => __( 'Viewport dimensions', 'performance-lab' ),
'type' => 'object',
Expand Down Expand Up @@ -142,7 +158,7 @@ function ilo_handle_rest_request( WP_REST_Request $request ) {
ilo_set_page_metric_storage_lock();

$page_metric = $request->get_json_params();
$result = ilo_store_page_metric( $page_metric );
$result = ilo_store_page_metric( $page_metric['url'], $page_metric['slug'], $request->get_json_params() );

if ( $result instanceof WP_Error ) {
return $result;
Expand All @@ -152,7 +168,7 @@ function ilo_handle_rest_request( WP_REST_Request $request ) {
array(
'success' => true,
'post_id' => $result,
'data' => ilo_parse_stored_page_metrics( ilo_get_page_metrics_post( $page_metric['url'] ) ), // TODO: Remove this debug data.
'data' => ilo_parse_stored_page_metrics( ilo_get_page_metrics_post( $page_metric['slug'] ) ), // TODO: Remove this debug data.
)
);
}

0 comments on commit f9a1493

Please sign in to comment.