-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for deflate and gzip compression #31
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,13 +7,16 @@ 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'); | ||
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we hit this will a server request being going unanswered? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @simonh1000, that is my read. If you'd like, I can pick this up and carry it to the finish line, including handling unsupported encodings. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd first like to understand why this feature is useful for people on this development-only server. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a fair question. I'm not entirely sure myself. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like @oderayi's use case was Lighthouse auditing before deploying to production. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm. auditing like this does not help you know what score you will get in production. I'm concerned that this is increasing the sense that this is a production server, which it absolutely is not. I'm inclined to drop this. |
||
} | ||
} | ||
|
||
function getPort(portNo) { | ||
if (portNo) { | ||
var portNum = parseInt(portNo); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you explain what raw.pipe does relative the existing code that uses res.write?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@simonh1000, the pipe here allows the file that is being read as a stream to be streamed directly to the response without the entire file being in memory at any point. It is a more efficient approach to serving files, especially if any of the static assets is large.