From 9a579f1b248dc49ea053b962defcbc21f447ee18 Mon Sep 17 00:00:00 2001 From: GatsbyJS Bot Date: Mon, 21 Feb 2022 02:41:39 -0700 Subject: [PATCH] fix(gatsby-core-utils): fix 304 when file does not exists (#34842) (#34888) (cherry picked from commit 1f3eee04d1b09c23f885341382699ab91c15ea3e) Co-authored-by: Ward Peeters --- .../src/__tests__/fetch-remote-file.js | 35 ++++++++++++++++--- .../src/fetch-remote-file.ts | 21 +++++------ 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/gatsby-core-utils/src/__tests__/fetch-remote-file.js b/packages/gatsby-core-utils/src/__tests__/fetch-remote-file.js index 5c846355e19b3..a385e286c0ccd 100644 --- a/packages/gatsby-core-utils/src/__tests__/fetch-remote-file.js +++ b/packages/gatsby-core-utils/src/__tests__/fetch-remote-file.js @@ -229,20 +229,20 @@ async function createMockCache(tmpDir) { describe(`fetch-remote-file`, () => { let cache - const cachePath = path.join(__dirname, `.cache-fetch`) + const cacheRoot = path.join(__dirname, `.cache-fetch`) + const cachePath = path.join(__dirname, `.cache-fetch`, `files`) beforeAll(async () => { // Establish requests interception layer before all tests. server.listen() cache = await createMockCache(cachePath) - await fs.ensureDir(cachePath) - storage.getDatabaseDir.mockReturnValue(cachePath) + storage.getDatabaseDir.mockReturnValue(cacheRoot) }) afterAll(async () => { await storage.closeDatabase() - await fs.remove(cachePath) + await fs.remove(cacheRoot) delete global.__GATSBY // Clean up after all tests are done, preventing this @@ -250,7 +250,7 @@ describe(`fetch-remote-file`, () => { server.close() }) - beforeEach(() => { + beforeEach(async () => { // simulate a new build each run global.__GATSBY = { buildId: global.__GATSBY?.buildId @@ -260,6 +260,9 @@ describe(`fetch-remote-file`, () => { gotStream.mockClear() fsMove.mockClear() urlCount.clear() + + await fs.remove(cachePath) + await fs.ensureDir(cachePath) }) it(`downloads and create a svg file`, async () => { @@ -359,6 +362,28 @@ describe(`fetch-remote-file`, () => { global.__GATSBY = currentGlobal }) + it(`handles 304 responses correctly when file does not exists`, async () => { + const currentGlobal = global.__GATSBY + global.__GATSBY = { buildId: `304-3` } + const filePath = await fetchRemoteFile({ + url: `http://external.com/dog-304.jpg`, + directory: cachePath, + }) + + await fs.remove(filePath) + + global.__GATSBY = { buildId: `304-4` } + const filePathCached = await fetchRemoteFile({ + url: `http://external.com/dog-304.jpg`, + directory: cachePath, + }) + + expect(filePathCached).toBe(filePath) + expect(fsMove).toBeCalledTimes(2) + expect(gotStream).toBeCalledTimes(2) + global.__GATSBY = currentGlobal + }) + it(`fails when 404 is triggered`, async () => { await expect( fetchRemoteFile({ diff --git a/packages/gatsby-core-utils/src/fetch-remote-file.ts b/packages/gatsby-core-utils/src/fetch-remote-file.ts index 966f31ccc362b..49daf66595417 100644 --- a/packages/gatsby-core-utils/src/fetch-remote-file.ts +++ b/packages/gatsby-core-utils/src/fetch-remote-file.ts @@ -1,6 +1,6 @@ import fileType from "file-type" import path from "path" -import fs from "fs-extra" +import fs, { pathExists } from "fs-extra" import Queue from "fastq" import { createContentDigest } from "./create-content-digest" import { @@ -148,14 +148,6 @@ async function fetchFile({ const cachedEntry = await storage.remoteFileInfo.get(url) - // See if there's response headers for this url - // from a previous request. - const headers = { ...httpHeaders } - if (cachedEntry?.headers?.etag) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - headers[`If-None-Match`] = cachedEntry.headers.etag - } - // Add htaccess authentication if passed in. This isn't particularly // extensible. We should define a proper API that we validate. const httpOptions: Options = {} @@ -176,6 +168,16 @@ async function fetchFile({ await fs.ensureDir(path.join(fileDirectory, digest)) const tmpFilename = createFilePath(fileDirectory, `tmp-${digest}`, ext) + const filename = createFilePath(path.join(fileDirectory, digest), name, ext) + + // See if there's response headers for this url + // from a previous request. + const headers = { ...httpHeaders } + if (cachedEntry?.headers?.etag && (await pathExists(filename))) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + headers[`If-None-Match`] = cachedEntry.headers.etag + } + const response = await requestRemoteNode( url, headers, @@ -183,7 +185,6 @@ async function fetchFile({ httpOptions ) - const filename = createFilePath(path.join(fileDirectory, digest), name, ext) if (response.statusCode === 200) { // Save the response headers for future requests. // If the user did not provide an extension and we couldn't get one from remote file, try and guess one