-
-
Notifications
You must be signed in to change notification settings - Fork 191
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
Add support for precompressed (for example gzip) content #108
base: master
Are you sure you want to change the base?
Conversation
Thanks, @gmokki! I have a question regarding the added functionality: If I were to enable |
I do not think there can be anything to prevent that (at least with this patch). It works the same way as apache/nginx etc when configured with support for precompressed resources. |
More concrete answer: Before this patch:
After this patch:
For applications using the |
Ah, @gmokki, gotcha. Yea, I looked at the nginx source and what they do is just unconditionally add |
Can you give a hint of what kind of extra documentation in addition to the short README.md documentation you would like to have. |
Hi @gmokki, currently the README change in this pull request is only the following:
There are a lot of questions that raises, including how to even use it. What values can I use for I'm not asking you these questions, they are just what comes to mind when reading the README. We'll need enough information in the README for someone to use the feature. Consider that you may take it for granted what "precompressed" means and how to lay it out, but many people have never done that before. |
I added the details you requested into the documentation with much longer clarifications. I also added a few generic tips on offline compression. |
index.js
Outdated
var header = this.req.headers['accept-encoding'] | ||
if (header) { | ||
this._precompressionFormats.forEach(function (format) { | ||
if (header.indexOf(format.encoding) >= 0) accepted.push(format.extension) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this is broken for web browsers that send "no-gzip, deflate", which was common for a time (Apache, and I assume others, understand no-gzip).
This also does not seem to take into account the quality parameters at all, which is very common from CDNs, as they usually want a specific encoding to store.
Thanks! I just realized I still have not actually looked at the implementation, so doing a little bit now. |
index.js
Outdated
} | ||
|
||
function sendPreferredContent(p, stat, contents) { | ||
if (contents.length) self.res.setHeader('Vary', 'Accept-Encoding') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will overwrite any other Vary already set. Let's append this to the field name if already set instead of overwriting.
This is a good feature to have. @gmokki can you can address the issues pointed out by @dougwilson? |
Yes, I will definitely try to address all pointed issues in next few days. Thank you for the through review and comments. |
Thanks, @gmokki! It sounds like this won't make it for the Express.js 4.14 release cut tonight, then. It'll probably be a few months before the next minor, and this module is locked to Express.js release schedules, so you'll have plenty of time to address! |
I just fixed all the issues that @dougwilson pointed out. I have already added a encoding quality parsing function to tomcat (https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/servlets/DefaultServlet.java#L1096) - I'll try to add it now to here too. |
Thanks, @gmokki! It looks like there is still the outstanding stat issue (unless you fixed it in one of the commits?). The quality issue is a real issue, at least from CDNs. I maintain the |
+1 for not rushing it. |
I just added code for the accept-encoding quality handling. I just added comment on why the stats are needed always when precompressed option is enabled. If that were not the case the precompression option could default to on. I think I have now really addressed all comments. Do provide more feedback on bugs/missing features and I'll try to address them too. |
Thanks, @gmokki! In order to get the 4.14 release ready for tonight and our TC meeting, I cannot look any more at this PR for now. I'll take another look tomorrow after 4.14 for sure. There is still the comment regarding the |
Also, the CI tests are still taking their sweet time to run (they still haven't gotten to your pushes from earlier today), so we have to at least wait on those to consider merging. Also, real quick, I do have a couple more thoughts on the header stuff: have you considered using the modules we have already owned by the Express TC to do some of that heavy lifting? Did they not work out (and would it be possible for us to modify them so they can be used here)? Most of them can be found in https://github.com/jshttp (our organizations are https://github.com/expressjs , https://github.com/pillarjs and https://github.com/jshttp). |
I'm not an authority on the brotli file formats nor their official extension. I originally tried to avoid extra dependencies. But I could use https://github.com/jshttp/vary to modify the vary header and https://github.com/jshttp/negotiator to parse the preferred accept-encoding header. I'll try to modify the code to use them tomorrow. Now that the code has tests for both cases taking them into use should be simple. |
That doesn't make it the correct thing to do. For example, I could have just accepted this and been in the same boat. Just because others do something doesn't automatically make it correct. In order to accept this PR as-is, with |
@dougwilson: I think you are correct that adding explicit brotli support is premature. The file format is not yet finalized. I just pushed a new version that removes brotli from the default set of formats and updates the documentation to match. I also tried using the https://github.com/jshttp/accepts for choosing the best encoding format. Unfortunately that does not yet work because the module does not honor the server preference order for values that have equal quality level in the client request. |
Is it possible to get that added? I think I remember seeing that wasn't in there when dealing with |
My negotiator pull request jshttp/negotiator#49 is still not merged, but I updated this pull request to use it. I also did some worst case benchmarking: when enabling the precompressed check with one extra stat the performance goes down at most by 20% when serving 1 byte files to localhost with single infinite depth http pipeline connection. I doubt that it is observable in actual use cases. |
…the browsers. - optimize png images with pngquant (quality 95) and advpng - optimize svg images with svgo - create precompressed variants of js, css, svg files: gz using zopfli and br using brotli Currently using forked versions of expressjs/send/negotiator while waiting for the PRs to go through: - jshttp/negotiator#49 - pillarjs/send#108
…the browsers. - optimize png images with pngquant (quality 95) and advpng - optimize svg images with svgo - create precompressed variants of js, css, svg files: gz using zopfli and br using brotli Currently using forked versions of expressjs/send/negotiator while waiting for the PRs to go through: - jshttp/negotiator#49 - pillarjs/send#108
…the browsers. - create precompressed variants of js, css, svg files: gz using zopfli and br using brotli Currently using forked versions of expressjs/send/negotiator while waiting for the PRs to go through: - jshttp/negotiator#49 - pillarjs/send#108
…the browsers. - create precompressed variants of js, css, svg files: gz using zopfli and br using brotli Currently using forked versions of expressjs/send/negotiator while waiting for the PRs to go through: - jshttp/negotiator#49 - pillarjs/send#108
Rebased to fix merge conflicts. Unfortunately the negotatior PR is still pending. |
Is there anything that could be done to aid in landing this PR? |
Disabled by default (should it be enabled by default?).
There should be no overhead if disabled.
If
opts.precompressed
is set to true, tries to serve both .br and .gz files, but custom configuration (encoding to extension mappings) is also supportedIf enabled then for every file that is served we look up each configured file extension (even if browser does not request compressed variants) and if any are found we set the
Vary
header. This is required for caching proxies that the clients my be using to work correctly.If matching file between
Accept-Encoding
and enabled precompression formats is found then the compressed file contents are served withContent-Encoding
header. If multiple supported matches are found the first configured in the preferences will be used.