Skip to content

Commit 22fac0d

Browse files
committed
refactor: refinement ⚗️
1 parent 08bf768 commit 22fac0d

File tree

8 files changed

+108
-98
lines changed

8 files changed

+108
-98
lines changed

API.md

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,35 +98,27 @@ assigned one or more (array):
9898

9999
#### <a name="server.options.compression" /> `server.options.compression`
100100

101-
Default value: `{ minBytes: 1024 }`.
101+
Default value: `{ encodings: { gzip: true, deflate: true, br: false, zstd: false }, minBytes: 1024 }`.
102102

103103
Defines server handling of content encoding requests. If `false`, response content encoding is
104-
disabled and no compression is performed by the server.
104+
disabled, and no compression is performed by the server.
105105

106-
##### <a name="server.options.compression.enableBrotli" /> `server.options.compression.enableBrotli`
106+
#### <a name="server.options.compression.encodings" /> `server.options.compression.encodings`
107107

108-
Default value: `false`.
109-
110-
Enables built-in support of `brotli` compression algorithm.
108+
Default value: `{ gzip: true, deflate: true, br: false, zstd: false }`.
111109

112-
Available values:
110+
Configures the built-in support of compression algorithms aka encodings, represented by the object of kv pairs.
113111

114-
- `false` - no compression.
115-
- `true` - compression with system defaults.
116-
- [`BrotliOptions`](https://nodejs.org/api/zlib.html#class-brotlioptions) - compression with specified options.
112+
Available values for each kv pair:
117113

118-
##### <a name="server.options.compression.enableZstd" /> `server.options.compression.enableZstd`
114+
- `true` - enables the encoding with default options.
115+
- `false` - disables the encoding.
116+
- `{...options}` - enables the encoding using custom options specific to each particular algorithm.
119117

120-
Default value: `false`.
121-
122-
Enables built-in support of `zstd` compression algorithm (node: `>=22.15.0`).
123118
Zstd compression is experimental (see [node Zstd documentation](https://nodejs.org/api/zlib.html#zlibcreatezstdcompressoptions)).
124119

125-
Available values:
126-
127-
- `false` - no compression.
128-
- `true` - compression with system defaults.
129-
- [`ZstdOptions`](https://nodejs.org/api/zlib.html#class-zstdoptions) - compression with specified options.
120+
Disabling an encoding allows custom compression algorithm to be applied by
121+
[`server.encoder()`](#server.encoder()) and [`server.decoder()`](#server.decoder()).
130122

131123
##### <a name="server.options.compression.minBytes" /> `server.options.compression.minBytes`
132124

lib/compression.js

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,64 @@ const internals = {
1616
'deflate, gzip',
1717
'gzip',
1818
'deflate'
19-
]
19+
],
20+
provision: new Map([
21+
['zstd', [
22+
(options) => Zlib.createZstdCompress(options),
23+
(options) => Zlib.createZstdDecompress(options),
24+
{
25+
params: {
26+
[Zlib.constants.ZSTD_c_compressionLevel]: 6
27+
}
28+
}
29+
]],
30+
['br', [
31+
(options) => Zlib.createBrotliCompress(options),
32+
(options) => Zlib.createBrotliDecompress(options),
33+
{
34+
params: {
35+
[Zlib.constants.BROTLI_PARAM_QUALITY]: 4
36+
}
37+
}
38+
]],
39+
['deflate', [
40+
(options) => Zlib.createDeflate(options),
41+
(options) => Zlib.createInflate(options)
42+
]],
43+
['gzip', [
44+
(options) => Zlib.createGzip(options),
45+
(options) => Zlib.createGunzip(options)
46+
]]
47+
])
2048
};
2149

2250

2351
exports = module.exports = internals.Compression = class {
2452

25-
decoders = {
26-
gzip: (options) => Zlib.createGunzip(options),
27-
deflate: (options) => Zlib.createInflate(options)
28-
};
53+
decoders = {};
2954

30-
encodings = ['identity', 'gzip', 'deflate'];
55+
encodings = ['identity'];
3156

3257
encoders = {
33-
identity: null,
34-
gzip: (options) => Zlib.createGzip(options),
35-
deflate: (options) => Zlib.createDeflate(options)
58+
identity: null
3659
};
3760

3861
#common = null;
3962

40-
constructor() {
63+
constructor({ compression }) {
4164

42-
this._updateCommons();
65+
if (!compression) {
66+
this._updateCommons();
67+
}
68+
69+
for (const [alg, [encoder, decoder, defaults = {}]] of internals.provision.entries()) {
70+
let conditions = compression?.encodings?.[alg];
71+
if (conditions) {
72+
conditions = Hoek.applyToDefaults(defaults, conditions);
73+
this.addEncoder(alg, (options = {}) => encoder(Hoek.applyToDefaults(conditions, options)));
74+
this.addDecoder(alg, (options = {}) => decoder(Hoek.applyToDefaults(conditions, options)));
75+
}
76+
}
4377
}
4478

4579
_updateCommons() {
@@ -125,29 +159,6 @@ exports = module.exports = internals.Compression = class {
125159
return encoder(request.route.settings.compression[encoding]);
126160
}
127161

128-
enableBrotliCompression(compressionOptions) {
129-
130-
const defaults = {
131-
params: {
132-
[Zlib.constants.BROTLI_PARAM_QUALITY]: 4
133-
}
134-
};
135-
compressionOptions = Hoek.applyToDefaults(defaults, compressionOptions);
136-
this.decoders.br = (options) => Zlib.createBrotliDecompress({ ...options, ...compressionOptions });
137-
this.encoders.br = (options) => Zlib.createBrotliCompress({ ...options, ...compressionOptions });
138-
this.setPriority(['br']);
139-
}
140-
141-
enableZstdCompression(compressionOptions) {
142-
143-
/* $lab:coverage:off$ */
144-
Hoek.assert(!!Zlib.constants.ZSTD_CLEVEL_DEFAULT, 'Zstd is not supported by the engine');
145-
this.decoders.zstd = (options) => Zlib.createZstdDecompress({ ...options, ...compressionOptions });
146-
this.encoders.zstd = (options) => Zlib.createZstdCompress({ ...options, ...compressionOptions });
147-
this.setPriority(['zstd']);
148-
/* $lab:coverage:on$ */
149-
}
150-
151162
setPriority(priority) {
152163

153164
this.encodings = [...new Set([...priority, ...this.encodings])];

lib/config.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,24 @@ internals.server = Validate.object({
241241
autoListen: Validate.boolean(),
242242
cache: Validate.allow(null), // Validated elsewhere
243243
compression: Validate.object({
244-
enableBrotli: Validate.alternatives([
245-
Validate.boolean(),
246-
Validate.object()
247-
]).default(false),
248-
enableZstd: Validate.alternatives([
249-
Validate.boolean(),
250-
Validate.object()
251-
]).default(false),
244+
encodings: Validate.object({
245+
gzip: Validate.alternatives([
246+
Validate.boolean(),
247+
Validate.object()
248+
]).default(true),
249+
deflate: Validate.alternatives([
250+
Validate.boolean(),
251+
Validate.object()
252+
]).default(true),
253+
br: Validate.alternatives([
254+
Validate.boolean(),
255+
Validate.object()
256+
]).default(false),
257+
zstd: Validate.alternatives([
258+
Validate.boolean(),
259+
Validate.object()
260+
]).default(false)
261+
}).default(),
252262
minBytes: Validate.number().min(1).integer().default(1024),
253263
priority: Validate.array().items(Validate.string().valid('gzip', 'deflate', 'br', 'zstd')).default(null)
254264
})

lib/core.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ exports = module.exports = internals.Core = class {
5454
app = {};
5555
auth = new Auth(this);
5656
caches = new Map(); // Cache clients
57-
compression = new Compression();
57+
compression = null;
5858
controlled = null; // Other servers linked to the phases of this server
5959
dependencies = []; // Plugin dependencies
6060
events = new Podium.Podium(internals.events);
@@ -119,6 +119,7 @@ exports = module.exports = internals.Core = class {
119119
this.settings = settings;
120120
this.type = type;
121121

122+
this.compression = new Compression(this.settings);
122123
this.heavy = new Heavy(this.settings.load);
123124
this.mime = new Mimos(this.settings.mime);
124125
this.router = new Call.Router(this.settings.router);
@@ -127,16 +128,6 @@ exports = module.exports = internals.Core = class {
127128
this._debug();
128129
this._initializeCache();
129130

130-
if (this.settings.compression.enableBrotli) {
131-
this.compression.enableBrotliCompression(this.settings.compression.enableBrotli);
132-
}
133-
134-
/* $lab:coverage:off$ */
135-
if (this.settings.compression.enableZstd) {
136-
this.compression.enableZstdCompression(this.settings.compression.enableZstd);
137-
}
138-
/* $lab:coverage:on$ */
139-
140131
if (this.settings.compression.priority) {
141132
this.compression.setPriority(this.settings.compression.priority);
142133
}

lib/types/server/options.d.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as http from 'http';
22
import * as https from 'https';
3-
import { BrotliOptions, ZstdOptions } from 'zlib';
3+
import { BrotliOptions, ZstdOptions, ZlibOptions } from 'zlib';
44

55
import { MimosOptions } from '@hapi/mimos';
66

@@ -10,10 +10,14 @@ import { CacheProvider, ServerOptionsCache } from './cache';
1010
import { SameSitePolicy, ServerStateCookieOptions } from './state';
1111

1212
export interface ServerOptionsCompression {
13-
enableBrotli: boolean | BrotliOptions;
14-
enableZstd: boolean | ZstdOptions;
13+
encodings?: {
14+
gzip?: boolean | ZlibOptions;
15+
deflate?: boolean | ZlibOptions;
16+
br?: boolean | BrotliOptions;
17+
zstd?: boolean | ZstdOptions;
18+
};
1519
minBytes: number;
16-
priority: string[];
20+
priority?: string[];
1721
}
1822

1923
/**

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"@hapi/somever": "^4.1.1",
4040
"@hapi/statehood": "^8.2.0",
4141
"@hapi/subtext": "^8.1.1",
42-
"@hapi/teamwork": "^6.0.0",
42+
"@hapi/teamwork": "^6.0.1",
4343
"@hapi/topo": "^6.0.2",
4444
"@hapi/validate": "^2.0.1"
4545
},
@@ -51,7 +51,7 @@
5151
"@hapi/lab": "^25.3.2",
5252
"@hapi/vision": "^7.0.3",
5353
"@hapi/wreck": "^18.1.0",
54-
"@types/node": "^22.17.0",
54+
"@types/node": "^22.18.3",
5555
"handlebars": "^4.7.8",
5656
"joi": "^17.13.3",
5757
"legacy-readable-stream": "npm:readable-stream@^1.1.14",

test/payload.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ describe('Payload', () => {
530530
it('handles br payload', async () => {
531531

532532
const message = { 'msg': 'This message is going to be brotlied.' };
533-
const server = Hapi.server({ compression: { enableBrotli: true } });
533+
const server = Hapi.server({ compression: { encodings: { br: true } } });
534534
server.route({ method: 'POST', path: '/', handler: (request) => request.payload });
535535

536536
const compressed = await new Promise((resolve) => Zlib.brotliCompress(JSON.stringify(message), (ignore, result) => resolve(result)));
@@ -554,7 +554,7 @@ describe('Payload', () => {
554554
it('handles zstd payload', { skip: !Common.hasZstd }, async () => {
555555

556556
const message = { 'msg': 'This message is going to be zstded.' };
557-
const server = Hapi.server({ compression: { enableZstd: true } });
557+
const server = Hapi.server({ compression: { encodings: { zstd: true } } });
558558
server.route({ method: 'POST', path: '/', handler: (request) => request.payload });
559559

560560
const compressed = await new Promise((resolve) => Zlib.zstdCompress(JSON.stringify(message), (ignore, result) => resolve(result)));

0 commit comments

Comments
 (0)