feat(server): implement brotli compression for server #17766
Closed
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Current Status: WIP
I'm new this repo so I need some help on figuring out how to run the tests and make sure that everything is working. Any guidance on how I should proceed would be very helpful.
Background
TL;DR
Next.js uses
expressjs/compression
to handle gzip compression for its HTTP server.There has been several attempts (172, 173, 158) to add brotli compression support to that library and so far none have been successfully merged (for various reasons). One of the largest reasons why this has been a challenge is because that library requires that any code is supported all the way down to Node 0.8. Also, it uses the
accepts
npm package which (although it is spec compliant) does not generally do what users actually want with regard to how it determines which compression encoding to use.I have re-written the
expressjs/compression
library in typescript and swapped out theaccetps
library for@hapi/accepts
which handles this issue in a more preferred way. I've also extended the library to add support for brotli compression. See the code here: https://github.com/nicksrandall/compressionWhat is the problem that is trying to be solved?
The brotli compression algorithm is generally more efficient than gzip and now supported in recent versions of Node.js and in all major browsers. Http clients (like web browsers) can specify an "Accept-Encoding" header in their requests to share which compression algorithms they support and prefer. According to the spec, when two values have the same preference, the first value will be used (seems reasonable right?).
However, many browsers (including Google Chrome) send the "br" (brotli) value last even though it is generally preferred to the other algorithms (ie they send
gzip, deflate, br
instead ofbr, gzip, deflate
). This causes the brotli compression algorithm to be de-prioritized and unused.What can we do about it?
We can follow a well-established convention to deviate from the spec slightly and force the server to choose the "preferred" compression algorithm when the client has (basically) stated that it doesn't not explicitly prefer one algorithm over another.
So, for example, if we get an "Accept-Encoding" header value of
gzip, deflate, br
we will usebr
(brotli) because brotli is more efficient over gzip and their preference (set by client) is both 1 (the default value).However, if we get
gzip;q=0.8, br;q=0.1
(q is level of preference from 0 to 1 where 1 is most preferred) we will use 'gzip' because the client has explicitly stated that it is more preferred than brotli.The
@hapi/accepts
library has this behavior by default so I'm using that as part of my compression library.