Skip to content

Commit a8baf91

Browse files
committed
#10 add better compression
1 parent 3915fd6 commit a8baf91

File tree

2 files changed

+115
-39
lines changed

2 files changed

+115
-39
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rtjscomp",
3-
"version": "0.9.5",
3+
"version": "0.9.6",
44
"description": "php-like server but with javascript",
55
"repository": {
66
"type": "git",

rtjscomp.js

Lines changed: 114 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ const querystring_parse = require('querystring').decode;
2020
const request_ip_get = require('ipware')().get_ip;
2121

2222
// constants
23+
const COMPRESS_METHOD_NONE = 0;
24+
const COMPRESS_METHOD_GZIP = 1;
25+
const COMPRESS_METHOD_BROTLI = 2;
26+
const COMPRESS_METHOD_ZSTD = 3;
2327
const GZIP_OPTIONS = {level: 9};
28+
const HAS_BROTLI = zlib.createBrotliCompress != null;
29+
const HAS_ZSTD = zlib.createZstdCompress != null;
2430
const HTTP_LIST_REG = /,\s*/;
2531
const IMPORT_REG = /\bimport\(/g;
2632
const IS_BUN = typeof Bun !== 'undefined';
@@ -38,13 +44,19 @@ const SERVICE_STATUS_STOPPING = 4; // running stop fn
3844
const SERVICE_STATUS_FAILED = 5; // waiting for fix
3945
const VERSION = require('./package.json').version;
4046
const WATCH_OPTIONS = {persistent: true, interval: 1000};
47+
const ZSTD_c_compressionLevel = 100;
48+
const ZSTD_OPTIONS = {
49+
params: {
50+
[ZSTD_c_compressionLevel]: 3,
51+
}
52+
}
4153

4254
// config
4355
const log_verbose_flag = process.argv.includes('-v');
4456
let log_verbose = log_verbose_flag;
4557
let port_http = 0;
4658
let port_https = 0;
47-
let gz_enabled = true;
59+
let compression_enabled = true;
4860
let exiting = false;
4961
/// any path -> file
5062
const path_aliases = new Map([
@@ -863,13 +875,7 @@ const request_handle = async (request, response, https) => {
863875
file_type_index + 1
864876
).toLowerCase();
865877

866-
let file_gz_enabled = (
867-
gz_enabled &&
868-
!request_method_head &&
869-
'accept-encoding' in request_headers &&
870-
!type_raws.has(file_type) &&
871-
request_headers['accept-encoding'].split(HTTP_LIST_REG).includes('gzip')
872-
);
878+
let file_compression = COMPRESS_METHOD_NONE;
873879

874880
const file_dyn_enabled = (
875881
type_dynamics.has(file_type) &&
@@ -1139,18 +1145,48 @@ const request_handle = async (request, response, https) => {
11391145
file_function_input['path'] = request_url_parsed.pathname;
11401146
file_function_input['user_agent'] = request_headers['user-agent'];
11411147

1142-
let file_function_output;
1148+
let file_function_output = response;
11431149
response.setHeader('Cache-Control', 'no-cache, no-store');
11441150

1145-
if (file_gz_enabled) {
1146-
response.setHeader('Content-Encoding', 'gzip');
1147-
1148-
(
1149-
file_function_output = zlib.createGzip(GZIP_OPTIONS)
1150-
).pipe(response);
1151-
}
1152-
else {
1153-
file_function_output = response;
1151+
if (
1152+
compression_enabled &&
1153+
'accept-encoding' in request_headers &&
1154+
!type_raws.has(file_type)
1155+
) {
1156+
const encodings = request_headers['accept-encoding'].split(HTTP_LIST_REG);
1157+
if (
1158+
HAS_ZSTD &&
1159+
encodings.includes('zstd')
1160+
) {
1161+
file_compression = COMPRESS_METHOD_ZSTD;
1162+
response.setHeader('Content-Encoding', 'zstd');
1163+
if (!request_method_head) {
1164+
(
1165+
file_function_output = zlib.createZstdCompress(ZSTD_OPTIONS)
1166+
).pipe(response);
1167+
}
1168+
}
1169+
else if (
1170+
HAS_BROTLI &&
1171+
encodings.includes('br')
1172+
) {
1173+
file_compression = COMPRESS_METHOD_BROTLI;
1174+
response.setHeader('Content-Encoding', 'br');
1175+
if (!request_method_head) {
1176+
(
1177+
file_function_output = zlib.createBrotliCompress()
1178+
).pipe(response);
1179+
}
1180+
}
1181+
else if (encodings.includes('gzip')) {
1182+
file_compression = COMPRESS_METHOD_GZIP;
1183+
response.setHeader('Content-Encoding', 'gzip');
1184+
if (!request_method_head) {
1185+
(
1186+
file_function_output = zlib.createGzip(GZIP_OPTIONS)
1187+
).pipe(response);
1188+
}
1189+
}
11541190
}
11551191

11561192
if (spam_enabled) spam('execute', [
@@ -1223,7 +1259,7 @@ const request_handle = async (request, response, https) => {
12231259
}ERROR!`);
12241260
}
12251261
}
1226-
else if (file_gz_enabled) {
1262+
else if (file_compression !== COMPRESS_METHOD_NONE) {
12271263
response.removeHeader('Content-Encoding');
12281264
}
12291265
if (typeof returned !== 'number') {
@@ -1241,35 +1277,67 @@ const request_handle = async (request, response, https) => {
12411277
file_function_output.end();
12421278
}
12431279
else { // static file
1244-
let file_data = null;
1280+
const compression_enabled_type = (
1281+
compression_enabled &&
1282+
!type_raws.has(file_type)
1283+
);
1284+
let path_real_send = path_real;
12451285

12461286
if (
1247-
file_gz_enabled &&
1248-
file_stat.size > 80 &&
1249-
fs.existsSync(path_real + '.gz')
1287+
compression_enabled_type &&
1288+
'accept-encoding' in request_headers
12501289
) {
1251-
file_data = fs.createReadStream(path_real + '.gz');
1252-
}
1253-
else {
1254-
file_gz_enabled = false;
1255-
file_data = fs.createReadStream(path_real);
1290+
const encodings = request_headers['accept-encoding'].split(HTTP_LIST_REG);
1291+
if (
1292+
encodings.includes('zstd') &&
1293+
fs.existsSync(path_real + '.zst')
1294+
) {
1295+
file_compression = COMPRESS_METHOD_ZSTD;
1296+
path_real_send += '.zst';
1297+
}
1298+
else if (
1299+
encodings.includes('br') &&
1300+
fs.existsSync(path_real + '.br')
1301+
) {
1302+
file_compression = COMPRESS_METHOD_BROTLI;
1303+
path_real_send += '.br';
1304+
}
1305+
else if (
1306+
encodings.includes('gzip') &&
1307+
fs.existsSync(path_real + '.gz')
1308+
) {
1309+
file_compression = COMPRESS_METHOD_GZIP;
1310+
path_real_send += '.gz';
1311+
}
12561312
}
12571313

1258-
if (spam_enabled) spam('static_send', [path, file_gz_enabled]);
1314+
if (spam_enabled) spam('static_send', [path, file_compression]);
12591315
response.setHeader('Cache-Control', 'public, max-age=600');
1260-
1261-
if (file_gz_enabled) {
1262-
response.setHeader('Content-Encoding', 'gzip');
1316+
if (compression_enabled_type) {
1317+
response.setHeader('Vary', 'Accept-Encoding');
12631318
}
1264-
else {
1319+
1320+
switch (file_compression) {
1321+
case COMPRESS_METHOD_NONE:
12651322
response.setHeader('Content-Length', file_stat.size);
1323+
break;
1324+
case COMPRESS_METHOD_GZIP:
1325+
response.setHeader('Content-Encoding', 'gzip');
1326+
break;
1327+
case COMPRESS_METHOD_BROTLI:
1328+
response.setHeader('Content-Encoding', 'br');
1329+
break;
1330+
case COMPRESS_METHOD_ZSTD:
1331+
response.setHeader('Content-Encoding', 'zstd');
1332+
break;
12661333
}
12671334

12681335
if (request_method_head) {
12691336
response.end();
12701337
}
12711338
else {
1272-
file_data.pipe(response);
1339+
fs.createReadStream(path_real_send)
1340+
.pipe(response);
12731341
}
12741342
}
12751343
}
@@ -1347,6 +1415,8 @@ log(`rtjscomp v${
13471415
process.platform
13481416
.replace('win32', 'windows')
13491417
}`);
1418+
if (log_verbose && !HAS_BROTLI) log('[hint] brotli not available');
1419+
if (log_verbose && !HAS_ZSTD) log('[hint] zstd not available');
13501420

13511421
await file_keep_new(PATH_CONFIG + 'init.js', async data => {
13521422
if (!data) return;
@@ -1554,7 +1624,7 @@ try {
15541624
}
15551625
}
15561626
catch (err) {
1557-
if (log_verbose) log('https: no cert, disabled');
1627+
if (log_verbose) log('[hint] https: no cert, disabled');
15581628
}
15591629

15601630
// config
@@ -1565,7 +1635,8 @@ await file_keep_new('rtjscomp.json', data => {
15651635
throw 'must contain {}';
15661636
}
15671637

1568-
const gzip_level_new = get_prop_uint(data, 'gzip_level', 9);
1638+
const compression_enabled_new = get_prop_bool(data, 'compress', true);
1639+
const gzip_level_new = get_prop_uint(data, 'gzip_level', 3);
15691640
const log_verbose_new = get_prop_bool(data, 'log_verbose', log_verbose_flag);
15701641
const path_aliases_new = get_prop_map(data, 'path_aliases');
15711642
const path_ghosts_new = get_prop_list(data, 'path_ghosts');
@@ -1577,6 +1648,7 @@ await file_keep_new('rtjscomp.json', data => {
15771648
const type_dynamics_new = get_prop_list(data, 'type_dynamics');
15781649
const type_mimes_new = get_prop_map(data, 'type_mimes');
15791650
const type_raws_new = get_prop_list(data, 'type_raws');
1651+
const zstd_level_new = get_prop_uint(data, 'zstd_level', 3);
15801652

15811653
if (data) {
15821654
const keys_left = Object.keys(data);
@@ -1587,15 +1659,19 @@ await file_keep_new('rtjscomp.json', data => {
15871659
if (gzip_level_new > 9) {
15881660
throw 'gzip_level > 9';
15891661
}
1662+
if (zstd_level_new > 19) {
1663+
throw 'zstd_level > 19';
1664+
}
15901665
if (
15911666
port_http_new > 65535 ||
15921667
port_https_new > 65535
15931668
) {
15941669
throw 'port > 65535';
15951670
}
15961671

1597-
gz_enabled = gzip_level_new > 0;
1598-
GZIP_OPTIONS.level = gzip_level_new;
1672+
compression_enabled = compression_enabled_new;
1673+
GZIP_OPTIONS.level = compression_enabled ? gzip_level_new : 0;
1674+
ZSTD_OPTIONS.params[ZSTD_c_compressionLevel] = zstd_level_new;
15991675
log_verbose = log_verbose_new;
16001676
if (path_ghosts_new) {
16011677
path_ghosts.clear();

0 commit comments

Comments
 (0)