Add Cache-Tag headers
This commit is contained in:
parent
5f2805c2e9
commit
db69375e9c
14
server.js
14
server.js
|
@ -1,12 +1,16 @@
|
|||
const path = require('path')
|
||||
const throng = require('throng')
|
||||
const { startServer } = require('./server/index')
|
||||
const createServer = require('./server/createServer')
|
||||
|
||||
const port = parseInt(process.env.PORT, 10) || 5000
|
||||
const publicDir = path.resolve(__dirname, 'build')
|
||||
|
||||
throng({
|
||||
workers: process.env.WEB_CONCURRENCY || 1,
|
||||
start: (id) => startServer({ id, port, publicDir }),
|
||||
lifetime: Infinity
|
||||
lifetime: Infinity,
|
||||
start: function (id) {
|
||||
const server = createServer()
|
||||
|
||||
server.listen(port, function () {
|
||||
console.log('Server #%s listening on port %s, Ctrl+C to stop', id, port)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,23 +1,34 @@
|
|||
/*eslint-disable no-console*/
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const http = require('http')
|
||||
const express = require('express')
|
||||
const cors = require('cors')
|
||||
const morgan = require('morgan')
|
||||
const middleware = require('./middleware')
|
||||
|
||||
const { fetchStats } = require('./cloudflare')
|
||||
const parseURL = require('./middleware/parseURL')
|
||||
const checkBlacklist = require('./middleware/checkBlacklist')
|
||||
const fetchPackage = require('./middleware/fetchPackage')
|
||||
const findFile = require('./middleware/findFile')
|
||||
const serveFile = require('./middleware/serveFile')
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
morgan.token('fwd', function (req) {
|
||||
return req.get('x-forwarded-for').replace(/\s/g, '')
|
||||
})
|
||||
|
||||
const sendHomePage = (publicDir) => {
|
||||
function sendHomePage(publicDir) {
|
||||
const html = fs.readFileSync(path.join(publicDir, 'index.html'), 'utf8')
|
||||
|
||||
return (req, res, next) => {
|
||||
fetchStats((error, stats) => {
|
||||
return function (req, res, next) {
|
||||
fetchStats(function (error, stats) {
|
||||
if (error) {
|
||||
next(error)
|
||||
} else {
|
||||
res.set('Cache-Control', 'public, max-age=60')
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'home'
|
||||
})
|
||||
|
||||
res.send(
|
||||
// Replace the __SERVER_DATA__ token that was added to the
|
||||
// HTML file in the build process (see scripts/build.js).
|
||||
|
@ -30,15 +41,13 @@ const sendHomePage = (publicDir) => {
|
|||
}
|
||||
}
|
||||
|
||||
const errorHandler = (err, req, res, next) => {
|
||||
res.status(500).send('<p>Internal Server Error</p>')
|
||||
function errorHandler(err, req, res, next) {
|
||||
res.status(500).type('text').send('Internal Server Error')
|
||||
console.error(err.stack)
|
||||
next(err)
|
||||
}
|
||||
|
||||
morgan.token('fwd', (req) => req.get('x-forwarded-for').replace(/\s/g, ''))
|
||||
|
||||
const createServer = (config) => {
|
||||
function createServer() {
|
||||
const app = express()
|
||||
|
||||
app.disable('x-powered-by')
|
||||
|
@ -53,20 +62,24 @@ const createServer = (config) => {
|
|||
app.use(errorHandler)
|
||||
app.use(cors())
|
||||
|
||||
app.get('/', sendHomePage(config.publicDir))
|
||||
app.get('/', sendHomePage('build'))
|
||||
|
||||
app.use(express.static(config.publicDir, {
|
||||
app.use(express.static('build', {
|
||||
maxAge: '365d'
|
||||
}))
|
||||
|
||||
app.use(middleware())
|
||||
app.use(parseURL)
|
||||
app.use(checkBlacklist)
|
||||
app.use(fetchPackage)
|
||||
app.use(findFile)
|
||||
app.use(serveFile)
|
||||
|
||||
const server = http.createServer(app)
|
||||
|
||||
// Heroku dynos automatically timeout after 30s. Set our
|
||||
// own timeout here to force sockets to close before that.
|
||||
// https://devcenter.heroku.com/articles/request-timeout
|
||||
server.setTimeout(25000, (socket) => {
|
||||
server.setTimeout(25000, function (socket) {
|
||||
const message = `Timeout of 25 seconds exceeded`
|
||||
|
||||
socket.end([
|
||||
|
@ -83,22 +96,4 @@ const createServer = (config) => {
|
|||
return server
|
||||
}
|
||||
|
||||
const defaultServerConfig = {
|
||||
id: 1,
|
||||
port: parseInt(process.env.PORT, 10) || 5000,
|
||||
publicDir: 'public'
|
||||
}
|
||||
|
||||
const startServer = (serverConfig = {}) => {
|
||||
const config = Object.assign({}, defaultServerConfig, serverConfig)
|
||||
const server = createServer(config)
|
||||
|
||||
server.listen(config.port, () => {
|
||||
console.log('Server #%s listening on port %s, Ctrl+C to stop', config.id, config.port)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createServer,
|
||||
startServer
|
||||
}
|
||||
module.exports = createServer
|
|
@ -5,7 +5,7 @@ const blacklist = require('../PackageBlacklist').blacklist
|
|||
*/
|
||||
function checkBlacklist(req, res, next) {
|
||||
if (blacklist.includes(req.packageName)) {
|
||||
res.status(403).send(`Package ${req.packageName} is blacklisted`)
|
||||
res.status(403).type('text').send(`Package ${req.packageName} is blacklisted`)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ function fetchPackage(req, res, next) {
|
|||
PackageInfo.get(req.packageName, function (error, packageInfo) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
return res.status(500).send(`Cannot get info for package "${req.packageName}"`)
|
||||
return res.status(500).type('text').send(`Cannot get info for package "${req.packageName}"`)
|
||||
}
|
||||
|
||||
if (packageInfo == null || packageInfo.versions == null)
|
||||
return res.status(404).send(`Cannot find package "${req.packageName}"`)
|
||||
return res.status(404).type('text').send(`Cannot find package "${req.packageName}"`)
|
||||
|
||||
req.packageInfo = packageInfo
|
||||
|
||||
|
@ -28,7 +28,7 @@ function fetchPackage(req, res, next) {
|
|||
PackageCache.get(req.packageConfig, function (error, outputDir) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
res.status(500).send(`Cannot fetch package ${req.packageSpec}`)
|
||||
res.status(500).type('text').send(`Cannot fetch package ${req.packageSpec}`)
|
||||
} else {
|
||||
req.packageDir = outputDir
|
||||
next()
|
||||
|
@ -42,7 +42,7 @@ function fetchPackage(req, res, next) {
|
|||
if (maxVersion) {
|
||||
res.redirect(PackageURL.create(req.packageName, maxVersion, req.filename, req.search))
|
||||
} else {
|
||||
res.status(404).send(`Cannot find package ${req.packageSpec}`)
|
||||
res.status(404).type('text').send(`Cannot find package ${req.packageSpec}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -51,7 +51,7 @@ function findFile(req, res, next) {
|
|||
console.error(error)
|
||||
|
||||
if (file == null) {
|
||||
res.status(404).send(`Cannot find file "${req.filename}" in package ${req.packageSpec}`)
|
||||
res.status(404).type('text').send(`Cannot find file "${req.filename}" in package ${req.packageSpec}`)
|
||||
} else if (stats.isDirectory() && req.pathname[req.pathname.length - 1] !== '/') {
|
||||
// Append / to directory URLs.
|
||||
res.redirect(`${req.pathname}/${req.search}`)
|
||||
|
@ -71,7 +71,7 @@ function findFile(req, res, next) {
|
|||
|
||||
if (queryMain) {
|
||||
if (!(queryMain in packageConfig))
|
||||
return res.status(404).send(`Cannot find field "${queryMain}" in ${req.packageSpec} package config`)
|
||||
return res.status(404).type('text').send(`Cannot find field "${queryMain}" in ${req.packageSpec} package config`)
|
||||
|
||||
mainFilename = packageConfig[queryMain]
|
||||
} else {
|
||||
|
@ -93,7 +93,7 @@ function findFile(req, res, next) {
|
|||
console.error(error)
|
||||
|
||||
if (file == null) {
|
||||
res.status(404).send(`Cannot find main file "${mainFilename}" in package ${req.packageSpec}`)
|
||||
res.status(404).type('text').send(`Cannot find main file "${mainFilename}" in package ${req.packageSpec}`)
|
||||
} else {
|
||||
req.file = file.replace(req.packageDir, '')
|
||||
req.stats = stats
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
const express = require('express')
|
||||
const parseURL = require('./parseURL')
|
||||
const checkBlacklist = require('./checkBlacklist')
|
||||
const fetchPackage = require('./fetchPackage')
|
||||
const findFile = require('./findFile')
|
||||
const serveFile = require('./serveFile')
|
||||
|
||||
/**
|
||||
* Creates and returns a function that can be used in the "request"
|
||||
* event of a standard node HTTP server. Supported URL schemes are:
|
||||
*
|
||||
* /history@1.12.5/umd/History.min.js (recommended)
|
||||
* /history@1.12.5 (package.json's main is implied)
|
||||
*
|
||||
* Additionally, the following URLs are supported but will return a
|
||||
* temporary (302) redirect:
|
||||
*
|
||||
* /history (redirects to version, latest is implied)
|
||||
* /history/umd/History.min.js (redirects to version, latest is implied)
|
||||
* /history@latest/umd/History.min.js (redirects to version)
|
||||
* /history@^1/umd/History.min.js (redirects to max satisfying version)
|
||||
*/
|
||||
function createRequestHandler() {
|
||||
const app = express.Router()
|
||||
|
||||
app.use(
|
||||
parseURL,
|
||||
checkBlacklist,
|
||||
fetchPackage,
|
||||
findFile,
|
||||
serveFile
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
module.exports = createRequestHandler
|
|
@ -19,12 +19,12 @@ function parseURL(req, res, next) {
|
|||
const url = PackageURL.parse(req.url)
|
||||
|
||||
if (url == null)
|
||||
return res.status(403).send(`Invalid URL: ${req.url}`)
|
||||
return res.status(403).type('text').send(`Invalid URL: ${req.url}`)
|
||||
|
||||
// Do not allow unrecognized query parameters because
|
||||
// some people use them to bust the cache.
|
||||
if (!queryIsValid(url.query))
|
||||
return res.status(403).send(`Invalid query: ${JSON.stringify(url.query)}`)
|
||||
return res.status(403).type('text').send(`Invalid query: ${JSON.stringify(url.query)}`)
|
||||
|
||||
req.packageName = url.packageName
|
||||
req.packageVersion = url.packageVersion
|
||||
|
|
|
@ -31,7 +31,7 @@ function sendFile(res, file, stats) {
|
|||
|
||||
stream.on('error', (error) => {
|
||||
console.error(error)
|
||||
res.status(500).send('There was an error serving this file')
|
||||
res.status(500).type('text').send('There was an error serving this file')
|
||||
})
|
||||
|
||||
stream.pipe(res)
|
||||
|
@ -46,15 +46,21 @@ function serveFile(req, res, next) {
|
|||
Metadata.get(req.packageDir, req.file, req.stats, MaximumDepth, function (error, metadata) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
res.status(500).send(`Cannot generate JSON metadata for ${req.packageSpec}${req.filename}`)
|
||||
res.status(500).type('text').send(`Cannot generate JSON metadata for ${req.packageSpec}${req.filename}`)
|
||||
} else {
|
||||
// Cache metadata for 1 year.
|
||||
res.set('Cache-Control', 'public, max-age=31536000').send(metadata)
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=31536000',
|
||||
'Cache-Tag': 'meta'
|
||||
}).send(metadata)
|
||||
}
|
||||
})
|
||||
} else if (req.stats.isFile()) {
|
||||
// Cache files for 1 year.
|
||||
res.set('Cache-Control', 'public, max-age=31536000')
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=31536000',
|
||||
'Cache-Tag': 'file'
|
||||
})
|
||||
|
||||
// TODO: use res.sendFile instead of our own sendFile?
|
||||
sendFile(res, path.join(req.packageDir, req.file), req.stats)
|
||||
|
@ -62,14 +68,17 @@ function serveFile(req, res, next) {
|
|||
generateDirectoryIndexHTML(req.packageInfo, req.packageVersion, req.packageDir, req.file, function (error, html) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
res.status(500).send(`Cannot generate index page for ${req.packageSpec}${req.filename}`)
|
||||
res.status(500).type('text').send(`Cannot generate index page for ${req.packageSpec}${req.filename}`)
|
||||
} else {
|
||||
// Cache HTML directory listings for 1 minute.
|
||||
res.set('Cache-Control', 'public, max-age=60').send(html)
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'index'
|
||||
}).send(html)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
res.status(403).send(`Cannot serve ${req.packageSpec}${req.filename}; it's not a file`)
|
||||
res.status(403).type('text').send(`Cannot serve ${req.packageSpec}${req.filename}; it's not a file`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue