From 19a01b5a1d43a16047af284e706e12fc033f183a Mon Sep 17 00:00:00 2001 From: Steven Oderayi Date: Sat, 10 Aug 2019 14:58:48 +0100 Subject: [PATCH] Add support for deflate and gzip compression --- lib/angular-http-server.js | 63 ++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/lib/angular-http-server.js b/lib/angular-http-server.js index b124e49..71e73f0 100755 --- a/lib/angular-http-server.js +++ b/lib/angular-http-server.js @@ -7,6 +7,7 @@ var pem = require("pem"); var https = require("https"); var http = require("http"); var opn = require("opn"); +var zlib = require('zlib'); var argv = require("minimist")(process.argv.slice(2)); const getFilePathFromUrl = require('./get-file-path-from-url'); @@ -14,6 +15,8 @@ const getFilePathFromUrl = require('./get-file-path-from-url'); const NO_PATH_FILE_ERROR_MESSAGE = "Error: index.html could not be found in the specified path "; +const COMPRESSIBLE_CONTENT_TYPES = ['application/javascript', 'application/json', 'text/html', 'text/css']; + // if using config file, load that first if (argv.config) { let configPath; @@ -72,7 +75,7 @@ if (useHttps) { } function start() { - server.listen(port, function() { + server.listen(port, function () { if (argv.open == true || argv.o) { opn((useHttps ? "https" : "http") + "://localhost:" + port); } @@ -102,24 +105,64 @@ function requestListener(req, res) { const safeFullFileName = getFilePathFromUrl(req.url, basePath, { baseHref }); - fs.stat(safeFullFileName, function(err, stats) { - var fileBuffer; + fs.stat(safeFullFileName, function (err, stats) { if (!err && stats.isFile()) { - fileBuffer = fs.readFileSync(safeFullFileName); - let ct = mime.lookup(safeFullFileName); + const ct = mime.lookup(safeFullFileName); + const acceptEncoding = req.headers['accept-encoding'] || ''; + log(`Sending ${safeFullFileName} with Content-Type ${ct}`); - res.writeHead(200, { "Content-Type": ct }); + + if (contentIsCompressible(ct, acceptEncoding)) { + sendCompressedResponse(res, ct, acceptEncoding, safeFullFileName); + } else { + const raw = fs.createReadStream(safeFullFileName); + res.writeHead(200, { + "Content-Type": ct + }); + raw.pipe(res); + } } else { log("Route %s, replacing with index.html", safeFullFileName); - fileBuffer = returnDistFile(); res.writeHead(200, { "Content-Type": "text/html" }); + res.write(returnDistFile()); + res.end(); } - - res.write(fileBuffer); - res.end(); }); } +function contentIsCompressible(contentType, acceptEncoding) { + // Note: This is not a conformant accept-encoding parser. + // See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 + if (COMPRESSIBLE_CONTENT_TYPES.includes(contentType) + && ( + /\bdeflate\b/.test(acceptEncoding) || + /\bgzip\b/.test(acceptEncoding) + )) { + return true; + } + return false; +} + +function sendCompressedResponse(res, contentType, encoding, safeFullFileName) { + const raw = fs.createReadStream(safeFullFileName); + + if (/\bgzip\b/.test(encoding)) { + res.writeHead(200, { + "Content-Encoding": "gzip", + "Content-Type": contentType + }); + raw.pipe(zlib.createGzip()).pipe(res); + } else if (/\bdeflate\b/.test(encoding)) { + res.writeHead(200, { + "Content-Type": "deflate", + "Content-Type": contentType + }); + raw.pipe(zlib.createDeflate()).pipe(res); + } else { + log("Unsupported encoding: ", encoding); + } +} + function getPort(portNo) { if (portNo) { var portNum = parseInt(portNo);