Plugin Directory

Changeset 2480423


Ignore:
Timestamp:
02/24/2021 08:15:34 AM (5 years ago)
Author:
imageengine
Message:

Improved CORS compatibility, Added Permissions-Policy

Location:
image-cdn
Files:
6 edited
6 copied

Legend:

Unmodified
Added
Removed
  • image-cdn/trunk/image-cdn.php

    r2464753 r2480423  
    1717 * Text Domain:       image-cdn
    1818 * License:           GPLv2 or later
    19  * Version:           1.1.0
     19 * Version:           1.1.1
    2020 */
     21
     22// Update this then you update "Requires at least" above!
     23define( 'IMAGE_CDN_MIN_WP', '4.6' );
     24
     25// Update this when you update the "Version" above!
     26define( 'IMAGE_CDN_VERSION', '1.1.1' );
    2127
    2228// Load plugin files.
     
    3036define( 'IMAGE_CDN_DIR', __DIR__ );
    3137define( 'IMAGE_CDN_BASE', plugin_basename( __FILE__ ) );
    32 define( 'IMAGE_CDN_MIN_WP', '3.8' );
    3338
    3439add_action( 'plugins_loaded', array( ImageEngine\ImageCDN::class, 'instance' ) );
  • image-cdn/trunk/imageengine/class-imagecdn.php

    r2464753 r2480423  
    11<?php
    22/**
    3  * This file contains the ImageCDN class
     3 * This file contains the ImageCDN class.
    44 *
    55 * @package ImageCDN
     
    2222
    2323    /**
    24      * Singleton Rewriter instance
     24     * Client hints.
     25     *
     26     * @var []string
     27     */
     28    private static $client_hints = array(
     29        'Viewport-Width',
     30        'Width',
     31        'DPR',
     32        /**
     33         * Disabled for CORS compatibility:
     34         * 'ECT',
     35         * 'Device-Memory',
     36         * 'RTT',
     37         * 'Downlink',
     38         */
     39    );
     40
     41    /**
     42     * Client hints that do not require CORS preflight confirmation.
     43     *
     44     * @var []string
     45     */
     46    private static $safe_client_hints = array(
     47        'Viewport-Width',
     48        'Width',
     49        'DPR',
     50    );
     51
     52    /**
     53     * Singleton Rewriter instance.
    2554     *
    2655     * @var Rewriter
    2756     */
    2857    private static $rewriter;
     58
     59    /**
     60     * If true, some functionality will be augmented to facilitate testing.
     61     *
     62     * @internal
     63     * @var bool
     64     */
     65    public static $tests_running = false;
     66
     67    /**
     68     * Captures headers written during unit testing.
     69     *
     70     * @internal
     71     * @var []string
     72     */
     73    public static $test_headers_written = array();
     74
     75    /**
     76     * Options that will be used during unit testing.
     77     *
     78     * @internal
     79     * @var []string
     80     */
     81    public static $test_options = array();
    2982
    3083    /**
     
    4194            add_filter( 'the_content', array( self::class, 'rewrite_html' ), 100 );
    4295
    43             // Resource hints.  Note that the 'wp_head' is disabled for the time being due to CORS incompatibility.
    44             // add_action( 'wp_head', array( self::class, 'add_head_tags' ), 0 );    .
     96            /**
     97             * Resource hints.  Note that the 'wp_head' is disabled for the time being due to CORS incompatibility.
     98             * add_action( 'wp_head', array( self::class, 'add_head_tags' ), 0 );
     99             */
    45100            add_action( 'send_headers', array( self::class, 'add_headers' ), 0 );
    46101
     
    63118
    64119    /**
     120     * Outputs an HTTP header.
     121     *
     122     * @param string $key HTTP header key.
     123     * @param string $value HTTP header value.
     124     */
     125    private static function header( $key, $value ) {
     126        $val = "$key: $value";
     127        if ( self::$tests_running ) {
     128            self::$test_headers_written[] = $val;
     129            return;
     130        }
     131
     132        header( $val );
     133    }
     134
     135    /**
    65136     * Add http headers for Client Hints, Feature Policy and Preconnect Resource Hint.
    66137     */
    67138    public static function add_headers() {
    68         // Add client hints.
    69         header( 'Accept-CH: viewport-width, width, device-memory, dpr, downlink, ect' );
     139        self::header( 'Accept-CH', strtolower( implode( ', ', self::$client_hints ) ) );
    70140
    71141        // Add resource hints and feature policy.
    72142        $options = self::get_options();
    73143        $host    = wp_parse_url( $options['url'], PHP_URL_HOST );
    74         if ( ! empty( $host ) ) {
    75             $protocol = ( is_ssl() ) ? 'https://' : 'http://';
    76             header( 'Link: <' . $protocol . $host . '>; rel=preconnect' );
    77             header( 'Feature-Policy: ch-viewport-width ' . $protocol . $host . '; ch-width ' . $protocol . $host . '; ch device-memory ' . $protocol . $host . '; ch-dpr ' . $protocol . $host . '; ch-downlink ' . $protocol . $host . '; ch-ect ' . $protocol . $host . ';' );
    78         }
     144        if ( empty( $host ) ) {
     145            return;
     146        }
     147
     148        $protocol = is_ssl() ? 'https' : 'http';
     149
     150        // Add Preconnect header.
     151        self::header( 'Link', "<{$protocol}://{$host}>; rel=preconnect" );
     152
     153        // Add Feature-Policy header.
     154        // @deprecated in favor of Permissions-Policy and will be removed once adaquate market
     155        // adoption has been reached (90-95%).
     156        $features = array();
     157        foreach ( self::$client_hints as $hint ) {
     158            $features[] = strtolower( "ch-{$hint} {$protocol}://{$host}" );
     159        }
     160        self::header( 'Feature-Policy', strtolower( implode( '; ', $features ) ) );
     161
     162        $permissions = array();
     163        foreach ( self::$client_hints as $hint ) {
     164            $permissions[] = strtolower( "ch-{$hint}=(\"{$protocol}://{$host}\")" );
     165        }
     166        // Add Permissions-Policy header.
     167        // This header replaced Feature-Policy in Chrome 88, released in January 2021.
     168        // @see https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md#appendix-big-changes-since-this-was-called-feature-policy .
     169        self::header( 'Permissions-Policy', strtolower( implode( ', ', $permissions ) ) );
    79170    }
    80171
     
    85176    public static function add_head_tags() {
    86177        // Add client hints.
    87         echo '    <meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width">' . "\n";
     178        echo '    <meta http-equiv="Accept-CH" content="' . esc_attr( implode( ', ', self::$client_hints ) ) . '">' . "\n";
    88179
    89180        // Add resource hints.
     
    118209        );
    119210    }
     211
     212    /**
     213     * Gets the list of active client hints.
     214     *
     215     * @return array client hints.
     216     */
     217    public static function get_client_hints() {
     218        return self::$client_hints;
     219    }
     220
     221    /**
     222     * Gets the list of safe client hints.
     223     *
     224     * @return array client hints.
     225     */
     226    public static function get_safe_client_hints() {
     227        return self::$safe_client_hints;
     228    }
     229
     230    /**
     231     * Gets the list of active unsafe client hints.
     232     *
     233     * @return array client hints.
     234     */
     235    public static function get_unsafe_client_hints() {
     236        return array_diff( self::$client_hints, self::$safe_client_hints );
     237    }
     238
    120239
    121240    /**
     
    269388     */
    270389    public static function get_options() {
     390        if ( self::$tests_running ) {
     391            return self::$test_options;
     392        }
    271393        return wp_parse_args( get_option( 'image_cdn' ), self::default_options() );
    272394    }
  • image-cdn/trunk/imageengine/class-rewriter.php

    r2464753 r2480423  
    116116     */
    117117    public function exclude_asset( $asset ) {
     118        $path = strtolower( wp_parse_url( $asset, PHP_URL_PATH ) );
     119
    118120        // Excludes.
    119121        foreach ( $this->excludes as $exclude ) {
    120             if ( $exclude && stripos( $asset, $exclude ) !== false ) {
     122            if ( '' === $exclude ) {
     123                continue;
     124            }
     125
     126            if ( false !== strpos( $path, strtolower( $exclude ) ) ) {
    121127                return true;
    122128            }
    123129        }
     130
    124131        return false;
    125132    }
  • image-cdn/trunk/imageengine/class-settings.php

    r2267011 r2480423  
    255255        if ( ! isset( $cdn_res['headers']['content-type'] ) ) {
    256256            $out['type']    = 'warning';
    257             $out['message'] = 'Unable to confirm that the CDN is working properly because it didn\'t send a content type';
     257            $out['message'] = 'Unable to confirm that the CDN is working properly because it didn\'t send Content-Type';
    258258            wp_send_json_error( $out );
    259259        }
     
    262262        if ( strpos( $cdn_type, 'image/png' ) === false ) {
    263263            $out['type']    = 'error';
    264             $out['message'] = "CDN returned the wrong content type (expected 'image/png', got '$cdn_type'";
    265             wp_send_json_error( $out );
    266         }
     264            $out['message'] = "CDN returned the wrong content type (expected 'image/png', got '$cdn_type')";
     265            wp_send_json_error( $out );
     266        }
     267
     268        /**
     269         * This check it commented out until we can confirm that it properly tests CORS functionality.
     270         *
     271         * $unsafe_hints = ImageCDN::get_unsafe_client_hints();
     272         * if ( 0 < count( $unsafe_hints ) ) {
     273         *     $cors_error = true;
     274         *     if ( ! array_key_exists( 'access-control-allowed-headers', $cdn_res['headers'] ) ) {
     275         *         $out['type']    = 'warning';
     276         *         $out['message'] = 'Unable to confirm that the CDN supports client-hints because it didn\'t send Access-Control-Allow-Headers.  Fonts may not work if served from the CDN.';
     277         *         wp_send_json_error( $out );
     278         *     }
     279         *
     280         *     $allowed       = preg_split( '/ +/', trim( $cdn_res['headers']['access-control-allowed-headers'] ) );
     281         *     $missing_hints = array_diff( $unsafe_hints, $allowed );
     282         *     if ( 0 < count( $missing_hints ) ) {
     283         *         $out['type']    = 'warning';
     284         *         $out['message'] = 'Unable to confirm that the CDN supports advanced client-hints because it is missing some active client-hints in Access-Control-Allow-Headers (' . implode( ',', $missing_hints ) . ').  Fonts may not work if served from the CDN.';
     285         *         wp_send_json_error( $out );
     286         *     }
     287         * }
     288         */
    267289
    268290        $out['type']    = 'success';
  • image-cdn/trunk/readme.txt

    r2464753 r2480423  
    121121== Changelog ==
    122122
     123= 1.1.1 =
     124* Improved CORS compatibility
     125* Removed downlink and ect hint
     126* Added Permissions-Policy header
     127
    123128= 1.1.0 =
    124129* Fixed compatibility issue with Divi
  • image-cdn/trunk/templates/settings.php

    r2464753 r2480423  
    1919            ?>
    2020        </p>
    21         <p><?php esc_html_e( 'To obtain an ImageEngine Delivery Address' ); ?>:</p>
     21        <p><?php esc_html_e( 'To obtain an ImageEngine Delivery Address:' ); ?></p>
    2222        <ol>
    2323            <li><a target="_blank" href="https://imageengine.io/signup/?website=<?php echo esc_attr( get_site_url() ); ?>&?utm_source=WP-plugin-settigns&utm_medium=page&utm_term=wp-imageengine&utm_campaign=wp-imageengine">Sign up for an ImageEngine account</a></li>
    2424            <li>
    25             <?php
     25                <?php
    2626                printf(
    2727                    // translators: 1: http code example 2: https code example.
     
    3535        <p>See <a href="https://imageengine.io/docs/setup/quick-start/?utm_source=WP-plugin-settigns&utm_medium=page&utm_term=wp-imageengine&utm_campaign=wp-imageengine" target="_blank">full documentation.</a></p>
    3636    </div>
    37     <h2><?php esc_html_e( 'Image CDN Settings', 'image-cdn' ); ?></h2>
     37    <h2>
     38        <?php
     39        printf(
     40            // translators: %s is the plugin version number.
     41            'Image CDN Settings (version %s)',
     42            esc_attr( IMAGE_CDN_VERSION )
     43        )
     44        ?>
     45    </h2>
    3846    <?php if ( $options['enabled'] && ! $is_runnable ) { ?>
    3947        <div class="notice notice-error">
     
    120128                        <label for="image_cdn_dirs">
    121129                            <input type="text" name="image_cdn[dirs]" id="image_cdn_dirs" value="<?php echo esc_attr( $options['dirs'] ); ?>" size="64" class="regular-text code" />
    122                             <?php esc_html_e( 'Default', 'image-cdn' ); ?>: <code><?php echo esc_html( $defaults['dirs'] ); ?></code>
    123                         </label>
    124 
    125                         <p class="description">
    126                             <?php esc_html_e( 'Assets in these directories will be pointed to the CDN URL. Enter the directories separated by', 'image-cdn' ); ?> <code>,</code>
     130                            <?php esc_html_e( 'Optional; Default:', 'image-cdn' ); ?> <code><?php echo esc_html( $defaults['dirs'] ); ?></code>
     131                        </label>
     132
     133                        <p class="description">
     134                            <?php esc_html_e( 'Assets in these directories will be served by the CDN. Enter the directories separated by', 'image-cdn' ); ?> <code>,</code>
    127135                        </p>
    128136                    </fieldset>
Note: See TracChangeset for help on using the changeset viewer.