Optional Server Compression (#8066)

* Add `compress` option to enable gzip compression in `next start`.

* Add compress option, defaulting to true

* Disable compression for serverless target

* Pin compression dep

Co-Authored-By: Joe Haddad <timer150@gmail.com>

* Pin compression types

Co-Authored-By: Joe Haddad <timer150@gmail.com>

* lockfile update for locked compression deps

* simplify compression middleware application

* add test for compression
This commit is contained in:
Jason Miller 2019-07-29 14:35:09 -04:00 committed by Joe Haddad
parent df213f7aab
commit 9659b4b555
6 changed files with 139 additions and 2 deletions

View file

@ -18,6 +18,7 @@ const defaultConfig: { [key: string]: any } = {
pageExtensions: ['tsx', 'ts', 'jsx', 'js'],
target: process.env.__NEXT_BUILDER_EXPERIMENTAL_TARGET || 'server',
poweredByHeader: true,
compress: true,
onDemandEntries: {
maxInactiveAge: 60 * 1000,
pagesBufferLength: 2,

View file

@ -3,6 +3,7 @@ import { IncomingMessage, ServerResponse } from 'http'
import { join, resolve, sep } from 'path'
import { parse as parseQs, ParsedUrlQuery } from 'querystring'
import { parse as parseUrl, UrlWithParsedQuery } from 'url'
import compression from 'compression'
import {
BUILD_ID_FILE,
@ -36,6 +37,12 @@ import { isBlockedPage, isInternalUrl } from './utils'
type NextConfig = any
type Middleware = (
req: IncomingMessage,
res: ServerResponse,
next: (err?: Error) => void
) => void
export type ServerConstructor = {
/**
* Where the Next project is located - @default '.'
@ -72,6 +79,7 @@ export default class Server {
documentMiddlewareEnabled: boolean
dev?: boolean
}
private compression?: Middleware
router: Router
private dynamicRoutes?: Array<{ page: string; match: RouteMatch }>
@ -101,6 +109,7 @@ export default class Server {
publicRuntimeConfig,
assetPrefix,
generateEtags,
compress,
} = this.nextConfig
this.buildId = this.readBuildId()
@ -122,6 +131,10 @@ export default class Server {
this.renderOpts.runtimeConfig = publicRuntimeConfig
}
if (compress && this.nextConfig.target !== 'serverless') {
this.compression = compression() as Middleware
}
// Initialize next/config with the environment configuration
envConfig.setConfig({
serverRuntimeConfig,
@ -371,11 +384,19 @@ export default class Server {
}))
}
private handleCompression(req: IncomingMessage, res: ServerResponse) {
if (this.compression) {
this.compression(req, res, () => {})
}
}
private async run(
req: IncomingMessage,
res: ServerResponse,
parsedUrl: UrlWithParsedQuery
) {
this.handleCompression(req, res)
try {
const fn = this.router.match(req, res, parsedUrl)
if (fn) {

View file

@ -76,6 +76,7 @@
"babel-plugin-transform-async-to-promises": "0.8.10",
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
"chalk": "2.4.2",
"compression": "1.7.4",
"find-up": "4.0.0",
"fork-ts-checker-webpack-plugin": "1.3.4",
"fresh": "0.5.2",
@ -119,6 +120,7 @@
"@types/babel__template": "7.0.2",
"@types/babel__traverse": "7.0.6",
"@types/cross-spawn": "6.0.0",
"@types/compression": "0.0.36",
"@types/etag": "1.8.0",
"@types/find-up": "2.1.1",
"@types/fresh": "0.5.0",

View file

@ -0,0 +1 @@
export default () => <div id='hello'>OK</div>

View file

@ -0,0 +1,30 @@
/* eslint-env jest */
/* global jasmine */
import { join } from 'path'
import {
fetchViaHTTP,
renderViaHTTP,
findPort,
launchApp,
killApp
} from 'next-test-utils'
const context = {}
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
describe('Compression', () => {
beforeAll(async () => {
context.appPort = await findPort()
context.server = await launchApp(join(__dirname, '../'), context.appPort)
// pre-build page at the start
await renderViaHTTP(context.appPort, '/')
})
afterAll(() => killApp(context.server))
it('should compress responses by default', async () => {
const res = await fetchViaHTTP(context.appPort, '/')
expect(res.headers.get('content-encoding')).toMatch(/gzip/)
})
})

View file

@ -1800,6 +1800,28 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/body-parser@*":
version "1.17.0"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c"
integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==
dependencies:
"@types/connect" "*"
"@types/node" "*"
"@types/compression@0.0.36":
version "0.0.36"
resolved "https://registry.yarnpkg.com/@types/compression/-/compression-0.0.36.tgz#7646602ffbfc43ea48a8bf0b2f1d5e5f9d75c0d0"
integrity sha512-B66iZCIcD2eB2F8e8YDIVtCUKgfiseOR5YOIbmMN2tM57Wu55j1xSdxdSw78aVzsPmbZ6G+hINc+1xe1tt4NBg==
dependencies:
"@types/express" "*"
"@types/connect@*":
version "3.4.32"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28"
integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==
dependencies:
"@types/node" "*"
"@types/content-type@1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@types/content-type/-/content-type-1.1.3.tgz#3688bd77fc12f935548eef102a4e34c512b03a07"
@ -1829,6 +1851,23 @@
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
"@types/express-serve-static-core@*":
version "4.16.7"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz#50ba6f8a691c08a3dd9fa7fba25ef3133d298049"
integrity sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==
dependencies:
"@types/node" "*"
"@types/range-parser" "*"
"@types/express@*":
version "4.17.0"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.0.tgz#49eaedb209582a86f12ed9b725160f12d04ef287"
integrity sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==
dependencies:
"@types/body-parser" "*"
"@types/express-serve-static-core" "*"
"@types/serve-static" "*"
"@types/find-up@2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@types/find-up/-/find-up-2.1.1.tgz#1cd2d240f1ad1f48d32346074724dc3107248a11"
@ -1960,6 +1999,11 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
"@types/range-parser@*":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
"@types/react-dom@16.8.4":
version "16.8.4"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.4.tgz#7fb7ba368857c7aa0f4e4511c4710ca2c5a12a88"
@ -1997,6 +2041,14 @@
"@types/mime" "*"
"@types/node" "*"
"@types/serve-static@*":
version "1.13.2"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48"
integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==
dependencies:
"@types/express-serve-static-core" "*"
"@types/mime" "*"
"@types/source-list-map@*":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
@ -2315,7 +2367,7 @@ abort-controller@3.0.0:
dependencies:
event-target-shim "^5.0.0"
accepts@~1.3.7:
accepts@~1.3.5, accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
@ -3250,6 +3302,11 @@ byte-size@^4.0.3:
resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-4.0.4.tgz#29d381709f41aae0d89c631f1c81aec88cd40b23"
integrity sha512-82RPeneC6nqCdSwCX2hZUz3JPOvN5at/nTEw/CMf05Smu3Hrpo9Psb7LjN+k+XndNArG1EY8L4+BM3aTM4BCvw==
bytes@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
@ -3848,6 +3905,26 @@ compress-commons@^1.2.0:
normalize-path "^2.0.0"
readable-stream "^2.0.0"
compressible@~2.0.16:
version "2.0.17"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==
dependencies:
mime-db ">= 1.40.0 < 2"
compression@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
dependencies:
accepts "~1.3.5"
bytes "3.0.0"
compressible "~2.0.16"
debug "2.6.9"
on-headers "~1.0.2"
safe-buffer "5.1.2"
vary "~1.1.2"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@ -8781,7 +8858,7 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0"
brorand "^1.0.1"
mime-db@1.40.0:
mime-db@1.40.0, "mime-db@>= 1.40.0 < 2":
version "1.40.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
@ -9480,6 +9557,11 @@ on-finished@~2.3.0:
dependencies:
ee-first "1.1.1"
on-headers@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"