const http2 = require('http2'); const fs = require('fs'); const path = require('path'); const zlib = require('zlib'); const brotli = require('brotli'); // npm package const PORT = 3032; const BROTLI_QUALITY = 11; // slow, but we're caching so who cares const STATIC_DIRECTORY = path.resolve(__dirname, '../dist/'); const cache = {}; /* -- To generate keys -- openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ -keyout localhost-privkey.pem -out localhost-cert.pem */ const server = http2.createSecureServer({ key: fs.readFileSync(path.resolve(__dirname, './localhost-privkey.pem')), cert: fs.readFileSync(path.resolve(__dirname, './localhost-cert.pem')), }); const getStaticFile = (fileName, useBrotli) => { const cacheKey = `${fileName}-${useBrotli}`; // we must cache gzipped and brotli responses separately if (cacheKey in cache) return cache[cacheKey]; const fileBuffer = fs.readFileSync(path.resolve(STATIC_DIRECTORY, fileName.replace(/^\//, ''))); const compressedFile = useBrotli ? brotli.compress(fileBuffer, { quality: BROTLI_QUALITY }) : zlib.gzipSync(fileBuffer.toString()); cache[cacheKey] = compressedFile; return compressedFile; }; const getItemAtPath = ({ path, useBrotli }) => { const notFoundResponse = { body: `

No ${path} for you!

`, responseHeader: { ':status': 404, }, }; try { const commonHeader = { 'Content-Encoding': useBrotli ? 'br' : 'gzip', ':status': 200, }; if (path === '/') { return { body: getStaticFile('index.html', useBrotli), responseHeader: { ...commonHeader, 'content-type': 'text/html', }, }; } if (path.endsWith('.js')) { return { body: getStaticFile(path, useBrotli), responseHeader: { ...commonHeader, 'Cache-Control': 'max-age=31536000', 'content-type': 'application/javascript', }, }; } if (path.endsWith('.css')) { return { body: getStaticFile(path, useBrotli), responseHeader: { ...commonHeader, 'Cache-Control': 'max-age=31536000', 'content-type': 'text/css', }, }; } return notFoundResponse; } catch (err) { return notFoundResponse; } } server.on('stream', (stream, headers) => { const { body, responseHeader } = getItemAtPath({ path: headers[':path'], useBrotli: headers['accept-encoding'].includes('br'), }) stream.respond(responseHeader); stream.end(body); }); server.listen(PORT); console.info(`Server is probably ready on https://localhost:${PORT}`);