Skip to content

Commit c940dcd

Browse files
authored
Middleware overhaul! (github#18218)
* Middleware overhaul! - Remove unnecessary 'async' keywords from middleware functions - Ensure all middleware functions we create have names - Wrap the method contents of all async middleware functions in a try-catch+next(error) pattern * Use asyncMiddleware wrapper instead of try-catch+next(error) pattern * Remove unnecessary try-catch+next(error) pattern from context middleware
1 parent 5f70c49 commit c940dcd

35 files changed

+91
-80
lines changed

Diff for: middleware/abort.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module.exports = function (req, res, next) {
1+
module.exports = function abort (req, res, next) {
22
// If the client aborts the connection, send an error
33
req.once('aborted', () => {
44
// NOTE: Node.js will also automatically set `req.aborted = true`

Diff for: middleware/archived-enterprise-versions-assets.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const ONE_DAY = 24 * 60 * 60 // 1 day in seconds
1111
//
1212
// See also ./archived-enterprise-versions.js for non-CSS/JS paths
1313

14-
module.exports = async (req, res, next) => {
14+
module.exports = async function archivedEnterpriseVersionsAssets (req, res, next) {
1515
const { isArchived, requestedVersion } = isArchivedVersion(req)
1616
if (!isArchived) return next()
1717

Diff for: middleware/archived-enterprise-versions.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const archivedFrontmatterFallbacks = require('../lib/redirects/static/archived-f
1111
// This module handles requests for deprecated GitHub Enterprise versions
1212
// by routing them to static content in help-docs-archived-enterprise-versions
1313

14-
module.exports = async (req, res, next) => {
14+
module.exports = async function archivedEnterpriseVersions (req, res, next) {
1515
const { isArchived, requestedVersion } = isArchivedVersion(req)
1616
if (!isArchived) return next()
1717

Diff for: middleware/block-robots.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function blockIndex (path) {
3030
return pathRegExps.some(pathRe => pathRe.test(path))
3131
}
3232

33-
const middleware = (req, res, next) => {
33+
const middleware = function blockRobots (req, res, next) {
3434
if (blockIndex(req.path)) res.set('x-robots-tag', 'noindex')
3535
return next()
3636
}

Diff for: middleware/breadcrumbs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const { getPathWithoutLanguage } = require('../lib/path-utils')
33
const nonEnterpriseDefaultVersion = require('../lib/non-enterprise-default-version')
44
const removeFPTFromPath = require('../lib/remove-fpt-from-path')
55

6-
module.exports = async (req, res, next) => {
6+
module.exports = async function breadcrumbs (req, res, next) {
77
if (!req.context.page) return next()
88
if (req.context.page.hidden) return next()
99

Diff for: middleware/categories-for-support-team.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const dotcomDir = path.join(__dirname, '../content/github')
88
const dotcomIndex = path.join(dotcomDir, 'index.md')
99
const linkRegex = /{% (?:topic_)?link_in_list ?\/(.*?) ?%}/g
1010

11-
module.exports = async (req, res, next) => {
11+
module.exports = async function categoriesForSupportTeam (req, res, next) {
1212
if (req.path !== '/categories.json') return next()
1313
const categories = await generateCategories()
1414
return res.json(categories)

Diff for: middleware/contextualizers/enterprise-release-notes.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async function renderPatchNotes (patch, ctx) {
6666
return patch
6767
}
6868

69-
module.exports = async (req, res, next) => {
69+
module.exports = async function enterpriseReleaseNotesContext (req, res, next) {
7070
// The `/release-notes` sub-path
7171
if (!req.path.endsWith('/release-notes')) return next()
7272

Diff for: middleware/contextualizers/graphql.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const explorerUrl = process.env.NODE_ENV === 'production'
88
? 'https://graphql.github.com/explorer'
99
: 'http://localhost:3000'
1010

11-
module.exports = async (req, res, next) => {
11+
module.exports = function graphqlContext (req, res, next) {
1212
const currentVersionObj = allVersions[req.context.currentVersion]
1313
// ignore requests to non-GraphQL reference paths
1414
// and to versions that don't exist

Diff for: middleware/contextualizers/rest.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const path = require('path')
22
const rest = require('../../lib/rest')
33
const removeFPTFromPath = require('../../lib/remove-fpt-from-path')
44

5-
module.exports = async function (req, res, next) {
5+
module.exports = function restContext (req, res, next) {
66
req.context.rest = rest
77

88
// link to include in `Works with GitHub Apps` notes

Diff for: middleware/contextualizers/webhooks.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const webhookPayloads = require(path.join(process.cwd(), 'lib/webhooks'))
44
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
55
const allVersions = require('../../lib/all-versions')
66

7-
module.exports = async (req, res, next) => {
7+
module.exports = function webhooksContext (req, res, next) {
88
const currentVersionObj = allVersions[req.context.currentVersion]
99
// ignore requests to non-webhook reference paths
1010
// and to versions that don't exist

Diff for: middleware/csp.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const versionSatisfiesRange = require('../lib/version-satisfies-range')
77
const AZURE_STORAGE_URL = 'githubdocs.azureedge.net'
88

99
// module.exports = contentSecurityPolicy({
10-
module.exports = async (req, res, next) => {
10+
module.exports = function csp (req, res, next) {
1111
const csp = {
1212
directives: {
1313
defaultSrc: ["'none'"],

Diff for: middleware/detect-language.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const languageCodes = Object.keys(require('../lib/languages'))
22

33
// determine language code from first part of URL, or default to English
4-
module.exports = async function detectLanguage (req, res, next) {
4+
module.exports = function detectLanguage (req, res, next) {
55
// /en/articles/foo
66
// ^^
77
const firstPartOfPath = req.path.split('/')[1]

Diff for: middleware/dev-toc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { liquid } = require('../lib/render-content')
22
const layouts = require('../lib/layouts')
33

4-
module.exports = async (req, res, next) => {
4+
module.exports = async function devToc (req, res, next) {
55
if (process.env.NODE_ENV !== 'development') return next()
66
if (!req.path.endsWith('/dev-toc')) return next()
77

Diff for: middleware/disable-caching-on-safari.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module.exports = (req, res, next) => {
1+
module.exports = function disableCachingOnSafari (req, res, next) {
22
const isSafari = /^((?!chrome|android).)*safari/i.test(req.headers['user-agent'])
33
if (isSafari) {
44
res.header('Last-Modified', (new Date()).toUTCString())

Diff for: middleware/early-access-breadcrumbs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const removeFPTFromPath = require('../lib/remove-fpt-from-path')
55

66
// Early Access content doesn't conform to the same structure as other products, so we
77
// can't derive breadcrumbs from the siteTree in the same way. Hence, this separate middleware.
8-
module.exports = async (req, res, next) => {
8+
module.exports = async function earlyAccessBreadcrumbs (req, res, next) {
99
if (!req.context.page) return next()
1010
if (!req.context.page.hidden) return next()
1111

Diff for: middleware/enterprise-server-releases.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const { liquid } = require('../lib/render-content')
22
const layouts = require('../lib/layouts')
33
const getMiniTocItems = require('../lib/get-mini-toc-items')
44

5-
module.exports = async (req, res, next) => {
5+
module.exports = async function enterpriseServerReleases (req, res, next) {
66
if (!req.path.endsWith('/enterprise-server-releases')) return next()
77

88
const html = await liquid.parseAndRender(layouts['enterprise-server-releases'], req.context)

Diff for: middleware/events.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const ajv = new Ajv()
1010

1111
const router = express.Router()
1212

13-
router.post('/', async (req, res, next) => {
13+
router.post('/', async function postEvents (req, res, next) {
1414
const isDev = process.env.NODE_ENV === 'development'
1515
const fields = omit(req.body, '_csrf')
1616

Diff for: middleware/featured-links.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const getLinkData = require('../lib/get-link-data')
22

33
// this middleware adds properties to the context object
4-
module.exports = async (req, res, next) => {
4+
module.exports = async function featuredLinks (req, res, next) {
55
if (!req.context.page) return next()
66

77
if (!(req.context.page.relativePath.endsWith('index.md') || req.context.page.layout === 'product-landing')) return next()

Diff for: middleware/handle-csrf-errors.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module.exports = async function handleCSRFError (error, req, res, next) {
1+
module.exports = function handleCSRFError (error, req, res, next) {
22
// If the CSRF token is bad
33
if (error.code === 'EBADCSRFTOKEN') {
44
return res.sendStatus(403)

Diff for: middleware/handle-errors.js

+40-35
Original file line numberDiff line numberDiff line change
@@ -28,47 +28,52 @@ async function logException (error, req) {
2828
}
2929

3030
module.exports = async function handleError (error, req, res, next) {
31-
// If the headers have already been sent or the request was aborted...
32-
if (res.headersSent || req.aborted) {
33-
// Report to Failbot
34-
await logException(error, req)
31+
try {
32+
// If the headers have already been sent or the request was aborted...
33+
if (res.headersSent || req.aborted) {
34+
// Report to Failbot
35+
await logException(error, req)
3536

36-
// We MUST delegate to the default Express error handler
37-
return next(error)
38-
}
37+
// We MUST delegate to the default Express error handler
38+
return next(error)
39+
}
3940

40-
// if the error is thrown before req.context is created (say, in the Page class),
41-
// set req.context.site here so we can pass data/ui.yml text to the 500 layout
42-
if (!req.context) {
43-
const site = await loadSiteData()
44-
req.context = { site: site[req.language || 'en'].site }
45-
}
41+
// if the error is thrown before req.context is created (say, in the Page class),
42+
// set req.context.site here so we can pass data/ui.yml text to the 500 layout
43+
if (!req.context) {
44+
const site = await loadSiteData()
45+
req.context = { site: site[req.language || 'en'].site }
46+
}
4647

47-
// display error on the page in development, but not in production
48-
if (process.env.NODE_ENV !== 'production' && req.context) {
49-
req.context.error = error
50-
}
48+
// display error on the page in development, but not in production
49+
if (process.env.NODE_ENV !== 'production' && req.context) {
50+
req.context.error = error
51+
}
5152

52-
// Special handling for when a middleware calls `next(404)`
53-
if (error === 404) {
54-
return res
55-
.status(404)
56-
.send(await liquid.parseAndRender(layouts['error-404'], req.context))
57-
}
53+
// Special handling for when a middleware calls `next(404)`
54+
if (error === 404) {
55+
return res
56+
.status(404)
57+
.send(await liquid.parseAndRender(layouts['error-404'], req.context))
58+
}
5859

59-
// If the error contains a status code, just send that back. This is usually
60-
// from a middleware like `express.json()` or `csrf`.
61-
if (error.statusCode || error.status) {
62-
return res.sendStatus(error.statusCode || error.status)
63-
}
60+
// If the error contains a status code, just send that back. This is usually
61+
// from a middleware like `express.json()` or `csrf`.
62+
if (error.statusCode || error.status) {
63+
return res.sendStatus(error.statusCode || error.status)
64+
}
6465

65-
if (process.env.NODE_ENV !== 'test') {
66-
console.error('500 error!', req.path)
67-
console.error(error)
68-
}
66+
if (process.env.NODE_ENV !== 'test') {
67+
console.error('500 error!', req.path)
68+
console.error(error)
69+
}
6970

70-
res.status(500).send(await liquid.parseAndRender(layouts['error-500'], req.context))
71+
res.status(500).send(await liquid.parseAndRender(layouts['error-500'], req.context))
7172

72-
// Report to Failbot AFTER responding to the user
73-
await logException(error, req)
73+
// Report to Failbot AFTER responding to the user
74+
await logException(error, req)
75+
} catch (error) {
76+
console.error('An error occurred in the error handling middleware!', error)
77+
return next(error)
78+
}
7479
}

Diff for: middleware/handle-invalid-paths.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const patterns = require('../lib/patterns')
22

3-
module.exports = (req, res, next) => {
3+
module.exports = function handleInvalidPaths (req, res, next) {
44
// prevent open redirect vulnerability
55
if (req.path.match(patterns.multipleSlashes)) {
66
return next(404)
@@ -10,12 +10,13 @@ module.exports = (req, res, next) => {
1010
// for paths like /%7B%
1111
try {
1212
decodeURIComponent(req.path)
13-
return next()
1413
} catch (err) {
1514
if (process.env.NODE_ENV !== 'test') {
1615
console.log('unable to decode path', req.path, err)
1716
}
1817

1918
return res.sendStatus(400)
2019
}
20+
21+
return next()
2122
}

Diff for: middleware/index.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ module.exports = function (app) {
6464
app.use(instrument('./redirects/handle-redirects')) // Must come before contextualizers
6565

6666
// *** Config and context for rendering ***
67-
app.use(instrument('./find-page')) // Must come before archived-enterprise-versions, breadcrumbs, featured-links, products, render-page
67+
app.use(asyncMiddleware(instrument('./find-page'))) // Must come before archived-enterprise-versions, breadcrumbs, featured-links, products, render-page
6868
app.use(instrument('./block-robots'))
6969

7070
// Check for a dropped connection before proceeding
7171
app.use(haltOnDroppedConnection)
7272

7373
// *** Rendering, 2xx responses ***
7474
// I largely ordered these by use frequency
75-
app.use(instrument('./archived-enterprise-versions-assets')) // Must come before static/assets
75+
app.use(asyncMiddleware(instrument('./archived-enterprise-versions-assets'))) // Must come before static/assets
7676
app.use('/dist', express.static('dist', {
7777
index: false,
7878
etag: false,
@@ -92,12 +92,12 @@ module.exports = function (app) {
9292
lastModified: false,
9393
maxAge: '7 days' // A bit longer since releases are more sparse
9494
}))
95-
app.use('/events', instrument('./events'))
96-
app.use('/search', instrument('./search'))
97-
app.use(instrument('./archived-enterprise-versions'))
95+
app.use('/events', asyncMiddleware(instrument('./events')))
96+
app.use('/search', asyncMiddleware(instrument('./search')))
97+
app.use(asyncMiddleware(instrument('./archived-enterprise-versions')))
9898
app.use(instrument('./robots'))
9999
app.use(/(\/.*)?\/early-access$/, instrument('./contextualizers/early-access-links'))
100-
app.use(instrument('./categories-for-support-team'))
100+
app.use(asyncMiddleware(instrument('./categories-for-support-team')))
101101
app.use(instrument('./loaderio-verification'))
102102
app.get('/_500', asyncMiddleware(instrument('./trigger-error')))
103103

@@ -109,12 +109,12 @@ module.exports = function (app) {
109109
app.use(instrument('./contextualizers/graphql'))
110110
app.use(instrument('./contextualizers/rest'))
111111
app.use(instrument('./contextualizers/webhooks'))
112-
app.use(instrument('./breadcrumbs'))
113-
app.use(instrument('./early-access-breadcrumbs'))
114-
app.use(instrument('./enterprise-server-releases'))
115-
app.use(instrument('./dev-toc'))
116-
app.use(instrument('./featured-links'))
117-
app.use(instrument('./learning-track'))
112+
app.use(asyncMiddleware(instrument('./breadcrumbs')))
113+
app.use(asyncMiddleware(instrument('./early-access-breadcrumbs')))
114+
app.use(asyncMiddleware(instrument('./enterprise-server-releases')))
115+
app.use(asyncMiddleware(instrument('./dev-toc')))
116+
app.use(asyncMiddleware(instrument('./featured-links')))
117+
app.use(asyncMiddleware(instrument('./learning-track')))
118118

119119
// *** Headers for pages only ***
120120
app.use(require('./set-fastly-cache-headers'))

Diff for: middleware/learning-track.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { getPathWithoutLanguage, getPathWithoutVersion } = require('../lib/path-utils')
22
const getLinkData = require('../lib/get-link-data')
33

4-
module.exports = async (req, res, next) => {
4+
module.exports = async function learningTrack (req, res, next) {
55
const noTrack = () => {
66
req.context.currentLearningTrack = {}
77
return next()

Diff for: middleware/loaderio-verification.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// prove to loader.io that we own this site
22
// by responding to requests like `/loaderio-12345/` with `loaderio-12345`
3-
module.exports = (req, res, next) => {
3+
module.exports = function loaderIoVerification (req, res, next) {
44
if (!req.path.startsWith('/loaderio-')) return next()
55
return res.send(req.path.replace(/\//g, ''))
66
}

Diff for: middleware/record-redirect.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const { v4: uuidv4 } = require('uuid')
22

3-
module.exports = function (req, res, next) {
3+
module.exports = function recordRedirects (req, res, next) {
44
if (!req.hydro.maySend()) return next()
55

66
res.on('finish', async function recordRedirect () {

Diff for: middleware/redirects/external.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const externalSites = require('../../lib/redirects/external-sites')
22

33
// blanket redirects to external websites
4-
module.exports = async function externalRedirects (req, res, next) {
4+
module.exports = function externalRedirects (req, res, next) {
55
if (req.path in externalSites) {
66
return res.redirect(301, externalSites[req.path])
77
} else {

Diff for: middleware/redirects/handle-redirects.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const patterns = require('../../lib/patterns')
22
const { URL } = require('url')
33

4-
module.exports = async function handleRedirects (req, res, next) {
4+
module.exports = function handleRedirects (req, res, next) {
55
// never redirect assets
66
if (patterns.assetPaths.test(req.path)) return next()
77

Diff for: middleware/redirects/help-to-docs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const patterns = require('../../lib/patterns')
33

44
// redirect help.github.com requests to docs.github.com
55

6-
module.exports = async (req, res, next) => {
6+
module.exports = function helpToDocs (req, res, next) {
77
if (req.hostname === 'help.github.com') {
88
// prevent open redirect security vulnerability
99
const path = req.originalUrl.replace(patterns.multipleSlashes, '/')

Diff for: middleware/redirects/language-code-redirects.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const languages = require('../../lib/languages')
55
// Examples:
66
// /jp* -> /ja*
77
// /zh-TW* -> /cn*
8-
module.exports = async function languageCodeRedirects (req, res, next) {
8+
module.exports = function languageCodeRedirects (req, res, next) {
99
for (const code in languages) {
1010
const language = languages[code]
1111
const redirectPatterns = language.redirectPatterns || []

Diff for: middleware/req-utils.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const Hydro = require('../lib/hydro')
22

3-
module.exports = (req, res, next) => {
3+
module.exports = function reqUtils (req, res, next) {
44
req.hydro = new Hydro()
55
return next()
66
}

0 commit comments

Comments
 (0)