Skip to content

Commit

Permalink
fix(#40066): preserve error status code from serveStatic (#40128)
Browse files Browse the repository at this point in the history
The PR fixes #40066.

Next.js' `serveStatic` method is powered by the [send](https://github.com/pillarjs/send) module, which could throw errors under specific circumstances.
Currently, Next.js only preserves the 412 Error from send, hence issue #40066 (where 416 Error is not preserved).

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`
  • Loading branch information
SukkaW committed Aug 31, 2022
1 parent 4cc7f11 commit 40767e5
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 2 deletions.
42 changes: 40 additions & 2 deletions packages/next/server/next-server.ts
Expand Up @@ -153,6 +153,41 @@ function getMiddlewareMatcher(
return matcher
}

/**
* Hardcoded every possible error status code that could be thrown by "serveStatic" method
* This is done by searching "this.error" inside "send" module's source code:
* https://github.com/pillarjs/send/blob/master/index.js
* https://github.com/pillarjs/send/blob/develop/index.js
*/
const POSSIBLE_ERROR_CODE_FROM_SERVE_STATIC = new Set([
// send module will throw 500 when header is already sent or fs.stat error happens
// https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L392
// Note: we will use Next.js built-in 500 page to handle 500 errors
// 500,

// send module will throw 404 when file is missing
// https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L421
// Note: we will use Next.js built-in 404 page to handle 404 errors
// 404,

// send module will throw 403 when redirecting to a directory without enabling directory listing
// https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L484
// Note: Next.js throws a different error (without status code) for directory listing
// 403,

// send module will throw 400 when fails to normalize the path
// https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L520
400,

// send module will throw 412 with conditional GET request
// https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L632
412,

// send module will throw 416 when range is not satisfiable
// https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L669
416,
])

function getEdgeMatcher(
info: MiddlewareManifest['functions'][string]
): RouteMatch {
Expand Down Expand Up @@ -1362,8 +1397,11 @@ export default class NextNodeServer extends BaseServer {
const err = error as Error & { code?: string; statusCode?: number }
if (err.code === 'ENOENT' || err.statusCode === 404) {
this.render404(req, res, parsedUrl)
} else if (err.statusCode === 412) {
res.statusCode = 412
} else if (
typeof err.statusCode === 'number' &&
POSSIBLE_ERROR_CODE_FROM_SERVE_STATIC.has(err.statusCode)
) {
res.statusCode = err.statusCode
return this.renderError(err, req, res, path)
} else {
throw err
Expand Down
10 changes: 10 additions & 0 deletions test/integration/file-serving/test/index.test.js
Expand Up @@ -68,6 +68,16 @@ const runTests = () => {
expect(res.headers.get('content-type')).toBe('image/avif')
})

it('should serve correct error code', async () => {
// vercel-icon-dark.avif is downloaded from https://vercel.com/design and transformed to avif on avif.io
const res = await fetchViaHTTP(appPort, '/vercel-icon-dark.avif', '', {
headers: {
Range: 'bytes=1000000000-',
},
})
expect(res.status).toBe(416) // 416 Range Not Satisfiable
})

// checks against traversal requests from
// https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Directory%20Traversal/Intruder/traversals-8-deep-exotic-encoding.txt

Expand Down

0 comments on commit 40767e5

Please sign in to comment.