From caf0791694938a5e90257e88676cf36191a5b3f4 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Fri, 11 Feb 2022 15:58:05 -0500 Subject: [PATCH] support archived enterprise assets (#25224) * support archived enterprise assets * better tests --- .../archived-enterprise-versions-assets.js | 55 ++++++++++- middleware/handle-errors.js | 18 +--- tests/rendering/events.js | 2 + tests/rendering/static-assets.js | 99 ++++++++++++++++++- 4 files changed, 154 insertions(+), 20 deletions(-) diff --git a/middleware/archived-enterprise-versions-assets.js b/middleware/archived-enterprise-versions-assets.js index 540aab8d5d..2b36d17c5e 100644 --- a/middleware/archived-enterprise-versions-assets.js +++ b/middleware/archived-enterprise-versions-assets.js @@ -15,12 +15,37 @@ const cacheControl = cacheControlFactory(60 * 60 * 24) // See also ./archived-enterprise-versions.js for non-CSS/JS paths export default async function archivedEnterpriseVersionsAssets(req, res, next) { + // Only match asset paths + // This can be true on /enterprise/2.22/_next/static/foo.css + // or /_next/static/foo.css + if (!patterns.assetPaths.test(req.path)) return next() + + // We now know the URL is either /enterprise/2.22/_next/static/foo.css + // or the regular /_next/static/foo.css. But we're only going to + // bother looking it up on https://github.github.com/help-docs-archived-enterprise-versions + // if the URL has the enterprise bit in it, or if the path was + // /_next/static/foo.css *and* its Referrer had the enterprise + // bit in it. + if ( + !( + patterns.getEnterpriseVersionNumber.test(req.path) || + patterns.getEnterpriseServerNumber.test(req.path) || + patterns.getEnterpriseVersionNumber.test(req.get('referrer')) || + patterns.getEnterpriseServerNumber.test(req.get('referrer')) + ) + ) { + return next() + } + + // Now we know the URL is definitely not /_next/static/foo.css + // So it's probably /enterprise/2.22/_next/static/foo.css and we + // should see if we might find this in the proxied backend. + // But `isArchivedVersion()` will only return truthy if the + // Referrer header also indicates that the request for this static + // asset came from a page const { isArchived, requestedVersion } = isArchivedVersion(req) if (!isArchived) return next() - // Only match asset paths - if (!patterns.assetPaths.test(req.path)) return next() - const assetPath = req.path.replace(`/enterprise/${requestedVersion}`, '') const proxyPath = path.join('/', requestedVersion, assetPath) @@ -37,6 +62,28 @@ export default async function archivedEnterpriseVersionsAssets(req, res, next) { cacheControl(res) return res.send(r.body) } catch (err) { - return next(404) + // Primarly for the developers working on tests that mock + // requests. If you don't set up `nock` correctly, you might + // not realize that and think it failed for other reasons. + if (err.toString().includes('Nock: No match for request')) { + throw err + } + + // It's important that we don't give up on this by returning a 404 + // here. It's better to let this through in case the asset exists + // beyond the realm of archived enterprise versions. + // For example, image you load + // /enterprise-server@2.21/en/DOES/NOT/EXIST in your browser. + // Quickly, we discover that the proxying is failing because + // it didn't find a page called `/en/DOES/NOT/EXIST` over there. + // So, we proceed to render *our* 404 HTML page. + // Now, on that 404 page, it will reference static assets too. + // E.g. + // These will thus be requested, with a Referrer header that + // forces us to give it a chance, but it'll find it can't find it + // but we mustn't return a 404 yet, because that + // /_next/static/styles.css will probably still succeed because the 404 + // page is not that of the archived enterprise version. + return next() } } diff --git a/middleware/handle-errors.js b/middleware/handle-errors.js index 1ecffc2dd6..f27f60b43d 100644 --- a/middleware/handle-errors.js +++ b/middleware/handle-errors.js @@ -41,21 +41,9 @@ export default async function handleError(error, req, res, next) { // By default, Fastly will cache 404 responses unless otherwise // told not to. // See https://docs.fastly.com/en/guides/how-caching-and-cdns-work#http-status-codes-cached-by-default - // Most of the time, that's a good thing! Especially, if bombarded - // for some static asset that we don't have. - // E.g. `ab -n 10000 https://docs.github.com/assets/doesnotexist.png` - // But due to potential timing issue related to how the servers start, - // what might happen is that a new insteance comes up that - // contains `