Ensure we don't poll page in development when notFound: true is returned (#34352)

Fixes: #34342

Visiting the following page will call gSSP indefinitely in a loop and logs errors from `on-demand-entries-client`:
```js
const Home = () => null
export default Home
        
export function getServerSideProps() {
  console.log("gssp called")
  return { notFound: true }
}
```

We should not keep fetching the page if it returns 404 as  it can introduce unnecessary data requests.

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`



Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
This commit is contained in:
Balázs Orbán 2022-02-16 19:53:48 +01:00 committed by GitHub
parent 7e93a89ba0
commit 9639fe704c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 1 deletions

View file

@ -11,7 +11,17 @@ export default async (page) => {
} else {
Router.ready(() => {
setInterval(() => {
sendMessage(JSON.stringify({ event: 'ping', page: Router.pathname }))
// when notFound: true is returned we should use the notFoundPage
// as the Router.pathname will point to the 404 page but we want
// to ping the source page that returned notFound: true instead
const notFoundSrcPage = self.__NEXT_DATA__.notFoundSrcPage
const pathname =
(Router.pathname === '/404' || Router.pathname === '/_error') &&
notFoundSrcPage
? notFoundSrcPage
: Router.pathname
sendMessage(JSON.stringify({ event: 'ping', page: pathname }))
}, 2500)
})
}

View file

@ -1550,6 +1550,9 @@ export default abstract class Server {
res.body('{"notFound":true}').send()
return null
} else {
if (this.renderOpts.dev) {
query.__nextNotFoundSrcPage = pathname
}
await this.render404(
req,
res,

View file

@ -528,9 +528,11 @@ export async function renderToHTML(
const headTags = (...args: any) => callMiddleware('headTags', args)
const isFallback = !!query.__nextFallback
const notFoundSrcPage = query.__nextNotFoundSrcPage
delete query.__nextFallback
delete query.__nextLocale
delete query.__nextDefaultLocale
delete query.__nextIsNotFound
const isSSG = !!getStaticProps
const isBuildTimeSSG = isSSG && renderOpts.nextExport
@ -1374,6 +1376,7 @@ export async function renderToHTML(
defaultLocale,
domainLocales,
isPreview: isPreview === true ? true : undefined,
notFoundSrcPage: notFoundSrcPage && dev ? notFoundSrcPage : undefined,
},
buildManifest: filteredBuildManifest,
docComponentsRendered,

View file

@ -53,6 +53,7 @@ export function addRequestMeta<K extends keyof RequestMeta>(
}
type NextQueryMetadata = {
__nextNotFoundSrcPage?: string
__nextDefaultLocale?: string
__nextFallback?: 'true'
__nextLocale?: string

View file

@ -107,6 +107,7 @@ export type NEXT_DATA = {
domainLocales?: DomainLocale[]
scriptLoader?: any[]
isPreview?: boolean
notFoundSrcPage?: string
rsc?: boolean
}

View file

@ -0,0 +1,34 @@
import { createNext } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { waitFor } from 'next-test-utils'
import webdriver from 'next-webdriver'
describe('getServerSideProps returns notFound: true', () => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
'pages/index.js': `
const Home = () => null
export default Home
export function getServerSideProps() {
console.log("gssp called")
return { notFound: true }
}
`,
},
dependencies: {},
})
})
afterAll(() => next.destroy())
it('should not poll indefinitely', async () => {
const browser = await webdriver(next.appPort, '/')
await waitFor(3000)
await browser.close()
const logOccurrences = next.cliOutput.split('gssp called').length - 1
expect(logOccurrences).toBe(1)
})
})