Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for gzip compression #184

Open
gameiqinc opened this issue Oct 31, 2013 · 24 comments
Open

Check for gzip compression #184

gameiqinc opened this issue Oct 31, 2013 · 24 comments

Comments

@gameiqinc
Copy link

Enhancement request to add compression option.

Most CDNs such as Amazon S3 can serve up gzip compressed files, but are unable to detect whether the browser supports gzip compression. To solve this issue we have to upload both compressed an uncompressed versions to the CDN and have the application decide which one to serve up, depending on what the browser supports.

Most often this can be determined by examining the HTTP_ACCEPT_ENCODING env variable.

It would be very useful if the AssetCompress plugin could automatically mange this. An option could be added to the ini file to govern this behavior. For example...

[global]
deflate=auto // Automatically manages gzip compression
deflate=true // Always compress
deflate=false // Never compress

If the deflate option is set a compression filter would be added to create an asset.type.gz file. The helper would then based on the browser's ability to support gzip compression automatically link to either the .gz file or the normal asset file.

@markstory
Copy link
Owner

What would the setting do? Would it generate .gz version of the minified assets?

@gameiqinc
Copy link
Author

Two things:

  1. Generate a gzipped version of the minified asset.
  2. More importantly instruct the helper to link to the correct version based on the availability of compression in the browser.

#2 is important because most of the time this is handled by the server. (such as the apache DEFLATE directive) But CDNs do not have this ability, thus it must be done by the application.

So $this->AssetCompress->css('style'); should link to style.css if gzip is not supported by the browser or style.css.gz if it is.

To determine if the browser supports gzip compression the helper could examine env('HTTP_ACCEPT_ENCODING') which usually contains the string 'gzip' if it is supported.

Something like this could do the trick.

<?php
if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], ‘gzip’))
  $compress=true; 
else 
  $compress=false;
?>

@gameiqinc
Copy link
Author

To illustrate the importance of this... My minified assets consist of 280.7 KB total in compressible text. With gzip compression this could be reduced to 88.9 KB. A potential savings of 191.7 KB. Very significant.

My only option right now is to not use asset compress because it can't manage this compression. Instead, I have to manually minify and compress my assets.It would be awesome if AssetCompress could handle this.

@markstory
Copy link
Owner

If you're dealing with a CDN is there any reason you wouldn't use non-gzipped assets? It sounds like generating gzipped files would be part of your build/deploy process. Replacing the existing files with gzipped files before pushing content to the CDN solves the problem at hand quite elegantly I think.

@gameiqinc
Copy link
Author

I think you're missing a key point. Yes, the CDN is capable of serving up gzipped files and yes you can replace the originals. However, this would mean I'm serving gzipped files for EVERY request. What if the request comes from a browser that doesn't support gzip decompression?

Therefore, AssetCompress->css() and AssetCompress-js() should add the .gz extension ONLY if we know the client can support it.

Does that make sense?

@markstory
Copy link
Owner

Yeah, but I'm not so sure the helper should magically add the gz extension. Perhaps it could be an option to script/CSS or a helper level switch that can be enabled as necessary by the application. Having the helper automatically decide could lead to confusion and there may be cases where other conditions are required to toggle gzipped assets.

@gameiqinc
Copy link
Author

I don't think there would be any confusion if the following options were added (globally or under each sub section as appropriate)...

[global|css|js]
deflate=true|false // Compress or not
compressed=gz // The extension of the compressed file

With these settings in place the compressed file could be created and included as appropriate.

AssetCompress already uses filters to manage minifying assets. Why not go a step further and compress them too. It's in the name after all. ;)

@markstory
Copy link
Owner

Sure but the config file is generally static and would lack the ability to toggle per request entirely. I would prefer to have the hot application tell the helper when to use a gzipped asset vs. guessing based on request headers, as the helper has no idea whether or not the webserver can also gzip the response.

@gameiqinc
Copy link
Author

The config file doesn't need to toggle anything. The helper toggels the link based on the following criteria:

A) If 'deflate' is true add compressed ext
B) If 'deflate' is false do not add compressed ext
C) If 'deflate' is auto and browser supports compression add compressed ext

This functionality is needed in AssetCompress when we know the webserver can't handle this. Like in the case of a CDN.

Again, it is impossible to use compression with CDNs if AssetCompress does not offer this feature.

@markstory
Copy link
Owner

My issue is with criteria C. I don't think the helper should be inspecting the request and making its own decisions. Instead I would much rather have the following

echo $this->AssetCompress->css('styles', array('gzip' => true));

Where the gzip option could be enabled by application logic that did the header detection or applied the other required criteria.

@gameiqinc
Copy link
Author

Thanks for giving this some thought.

AssetCompress already makes decisions based on a combination of settings and environmental factors. It includes files differently based on timestamps, filters, debug level, etc. Why is making a decision based on env('HTTP_ACCEPT_ENCODING') any different?

Your suggested solution is not sufficient. Compression must be selected based on the browser's ability to support it. This decision is normally made by the web server based on the headers it receives from the browser. A CDN is incapable of making this decision therefore the responsibility falls on the application.

Since this must be an application based decision it is appropriate for AssetCompress to provide the functionality. Perhaps these articles can describe what I'm talking about better:

http://internetmarketingbyme.com/74/gzip-compression-using-amazon-s3-cloudfront-cdn/

http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html

@markstory
Copy link
Owner

How is my solution not sufficient? Replacing the hard coded true with a variable generated by the application allows files to be conditionally loaded without the plugin needing to infer even more about what the developer wants.

None of the current decisions AssetCompress involve inspecting the request data. They are all a combination of configuration and filesystem contents. I'd like to refrain from adding more inference to the plugin. From my past experience every time I add more inference it causes problems for someone somewhere.

@derekperkins
Copy link

I'm with Mark on this one. I think your idea about getting this to work is
a fantastic one, and having a gzip parameter to add to assetCompress is a
great way to add the functionality without forcing a specific
implementation on people.

On Sat, Nov 2, 2013 at 11:35 AM, Mark Story notifications@github.comwrote:

How is my solution not sufficient? Replacing the hard coded true with a
variable generated by the application allows files to be conditionally
loaded without the plugin needing to infer even more about what the
developer wants.

None of the current decisions AssetCompress involve inspecting the request
data. They are all a combination of configuration and filesystem contents.
I'd like to refrain from adding more inference to the plugin. From my past
experience every time I add more inference it causes problems for someone
somewhere.


Reply to this email directly or view it on GitHubhttps://github.com//issues/184#issuecomment-27626846
.

@gameiqinc
Copy link
Author

Your solution is not sufficient because, unless I misunderstood, if the 'gzip' option is set to true compression would always be added. If the browser doesn't support compression it would not be able to load the compressed file and the site would break.

@derekperkins
Copy link

No, the logic about whether to gzip assets happens outside assetCompress,
and you tell it per instance whether or not to gzip.

On Sat, Nov 2, 2013 at 11:38 AM, gunner1095 notifications@github.comwrote:

Your solution is not sufficient because, unless I misunderstood, if the
'gzip' option is set to true compression would always be added. If the
browser doesn't support compression it would not be able to load the
compressed file and the site would break.


Reply to this email directly or view it on GitHubhttps://github.com//issues/184#issuecomment-27626916
.

@gameiqinc
Copy link
Author

If 'gzip' => true will serve gzipped files to those browsers that can support it and uncompressed to those that can't than it will work.

@gameiqinc
Copy link
Author

This is why I suggested gzip => auto or true or false to give the developer the flexibility to decide which way they want to go.

@markstory
Copy link
Owner

@gunner1095 No as a user you would set the gzip option to be what your application decides it should be. Meaning that the application needs to check the headers/apply any required logic. For example:

echo $this->AssetCompress->css('styles', array('gzip' => $useGzipAssets));

Would be more representative of how linking assets would look in an application. The $useGzipAssets value would need to be generated by the application.

@gameiqinc
Copy link
Author

Ok, so something like this?

if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], ‘gzip’)) {
   $gzip = true;
} else {
   $gzip = false;
}
echo $this->AssetCompress->css('styles', array('gzip' => $gzip));

@gameiqinc
Copy link
Author

@markstory, yeah the lightbulb finally went on!

@markstory
Copy link
Owner

Sure if those are the conditions your application needs then that would be one way of doing it. My concerns with hardcoding environment checks into the helper are around applications behind load-balancers or reverse proxies. Not all proxies forward the headers in the same way. Hardcoding the environment value in the helper makes it harder to accommodate various hosting/deployment setups.

@gameiqinc
Copy link
Author

Ok, got it. So, it looks to be fairly easy to add a filter to gzipcompress the output with something like this.

Filter/Gzip.php

App::uses('AssetFilter', 'AssetCompress.Lib');

class Gzip extends AssetFilter {

    protected $_settings = array(
            'compression_level' => 9
    );

    public function output($file, $contents) {
        return gzcompress($contents, $this->_settings['compression_level']);
    }

}

Although this would need to be modified to add the gz extension.

Then, the option to serve gzip compressed versions can be added to the helper.

@dereuromark
Copy link
Contributor

@gunner1095
What would be the advantage of this custom Gzip filter over letting the apache/nginx setting apply browser-dependent auto-gzip-ing?

@coryjthompson
Copy link

@dereuromark
I believe the advantage comes when you are offloading your static assets to something like S3 who don't offer auto-gzip-ing.

This would also be useful for me, so i'll look into it a bit more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants