1
0
mirror of synced 2025-12-23 11:54:18 -05:00

Add timeout and abort middleware and processing halts (#18177)

* Add middleware to timeout requests after a period

* Add halt-on-dropped-connection middleware to stop the middleware processing stack if the connection was already dropped

* Add a few strategic bail-out spots for dropped connections during the render-page middleware

* Handle 404s and HEAD requests earlier in the page rendering flow

* Add a few more strategic bail-out spots for dropped connections during the render-page middleware

* Add middleware to notice aborted requests

* Add a check for aborted requests into the isConnectionDropped logic

* Reformat comment for consistency

* Handle aborted requests correctly in the error handler

* Explicit returns for consistency
This commit is contained in:
James M. Greene
2021-03-09 13:14:02 -06:00
committed by GitHub
parent 1ee4d3f670
commit fd7d0eeb1a
8 changed files with 126 additions and 29 deletions

View File

@@ -6,6 +6,7 @@ const getMiniTocItems = require('../lib/get-mini-toc-items')
const Page = require('../lib/page')
const statsd = require('../lib/statsd')
const RedisAccessor = require('../lib/redis-accessor')
const { isConnectionDropped } = require('./halt-on-dropped-connection')
const { HEROKU_RELEASE_VERSION } = process.env
const pageCacheDatabaseNumber = 1
@@ -28,31 +29,6 @@ function addCsrf (req, text) {
module.exports = async function renderPage (req, res, next) {
const page = req.context.page
// Remove any query string (?...) and/or fragment identifier (#...)
const { pathname, searchParams } = new URL(req.originalUrl, 'https://docs.github.com')
for (const queryKey in req.query) {
if (!cacheableQueries.includes(queryKey)) {
searchParams.delete(queryKey)
}
}
const originalUrl = pathname + ([...searchParams].length > 0 ? `?${searchParams}` : '')
// Serve from the cache if possible (skip during tests)
const isCacheable = !process.env.CI && process.env.NODE_ENV !== 'test' && req.method === 'GET'
// Is the request for JSON debugging info?
const isRequestingJsonForDebugging = 'json' in req.query && process.env.NODE_ENV !== 'production'
if (isCacheable && !isRequestingJsonForDebugging) {
const cachedHtml = await pageCache.get(originalUrl)
if (cachedHtml) {
console.log(`Serving from cached version of ${originalUrl}`)
statsd.increment('page.sent_from_cache')
return res.send(addCsrf(req, cachedHtml))
}
}
// render a 404 page
if (!page) {
if (process.env.NODE_ENV !== 'test' && req.context.redirectNotFound) {
@@ -70,15 +46,52 @@ module.exports = async function renderPage (req, res, next) {
return res.status(200).end()
}
// Remove any query string (?...) and/or fragment identifier (#...)
const { pathname, searchParams } = new URL(req.originalUrl, 'https://docs.github.com')
for (const queryKey in req.query) {
if (!cacheableQueries.includes(queryKey)) {
searchParams.delete(queryKey)
}
}
const originalUrl = pathname + ([...searchParams].length > 0 ? `?${searchParams}` : '')
// Serve from the cache if possible (skip during tests)
const isCacheable = !process.env.CI && process.env.NODE_ENV !== 'test' && req.method === 'GET'
// Is the request for JSON debugging info?
const isRequestingJsonForDebugging = 'json' in req.query && process.env.NODE_ENV !== 'production'
if (isCacheable && !isRequestingJsonForDebugging) {
// Stop processing if the connection was already dropped
if (isConnectionDropped(req, res)) return
const cachedHtml = await pageCache.get(originalUrl)
if (cachedHtml) {
// Stop processing if the connection was already dropped
if (isConnectionDropped(req, res)) return
console.log(`Serving from cached version of ${originalUrl}`)
statsd.increment('page.sent_from_cache')
return res.send(addCsrf(req, cachedHtml))
}
}
// add page context
const context = Object.assign({}, req.context, { page })
// collect URLs for variants of this page in all languages
context.page.languageVariants = Page.getLanguageVariants(req.path)
// Stop processing if the connection was already dropped
if (isConnectionDropped(req, res)) return
// render page
context.renderedPage = await page.render(context)
// Stop processing if the connection was already dropped
if (isConnectionDropped(req, res)) return
// get mini TOC items on articles
if (page.showMiniToc) {
context.miniTocItems = getMiniTocItems(context.renderedPage, page.miniTocMaxHeadingLevel)