Skip to content

Commit

Permalink
EF: Don't crash CLI when uncompressed request throws uncaught excepti…
Browse files Browse the repository at this point in the history
…on (#5837)

* chore: add test

* fix: add content negotiation to error decoding
  • Loading branch information
Skn0tt authored Jul 5, 2023
1 parent 7a9f1cd commit f7291d1
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 2 deletions.
24 changes: 22 additions & 2 deletions src/utils/proxy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,29 @@ import { generateRequestID } from './request-id.mjs'
import { createRewriter, onChanges } from './rules-proxy.mjs'
import { signRedirect } from './sign-redirect.mjs'

const decompress = util.promisify(zlib.gunzip)
const gunzip = util.promisify(zlib.gunzip)
const brotliDecompress = util.promisify(zlib.brotliDecompress)
const deflate = util.promisify(zlib.deflate)
const shouldGenerateETag = Symbol('Internal: response should generate ETag')

/**
* @param {Buffer} body
* @param {string | undefined} contentEncoding
* @returns {Promise<Buffer>}
*/
const decompressResponseBody = async function (body, contentEncoding = '') {
switch (contentEncoding) {
case 'gzip':
return await gunzip(body)
case 'br':
return await brotliDecompress(body)
case 'deflate':
return await deflate(body)
default:
return body
}
}

const formatEdgeFunctionError = (errorBuffer, acceptsHtml) => {
const {
error: { message, name, stack },
Expand Down Expand Up @@ -479,7 +499,7 @@ const initializeProxy = async function ({ configPath, distDir, env, host, port,

if (isEdgeFunctionsRequest(req) && isUncaughtError) {
const acceptsHtml = req.headers && req.headers.accept && req.headers.accept.includes('text/html')
const decompressedBody = await decompress(responseBody)
const decompressedBody = await decompressResponseBody(responseBody, req.headers['content-encoding'])
const formattedBody = formatEdgeFunctionError(decompressedBody, acceptsHtml)
const errorResponse = acceptsHtml
? await renderErrorTemplate(formattedBody, './templates/function-error.html', 'edge function')
Expand Down
30 changes: 30 additions & 0 deletions tests/integration/100.command.dev.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,36 @@ test('When an edge function fails, serves a fallback defined by its `on_error` m
})
})

test('When an edge function throws uncaught exception, the dev server continues working', async (t) => {
await withSiteBuilder('site-with-edge-function-uncaught-exception', async (builder) => {
builder
.withNetlifyToml({
config: {
build: {
edge_functions: 'netlify/edge-functions',
},
},
})
.withEdgeFunction({
config: { path: '/hello' },
handler: () => {
const url = new URL('/shouldve-provided-a-base')
return new Response(url.toString())
},
name: 'hello-1',
})

await builder.buildAsync()

await withDevServer({ cwd: builder.directory }, async (server) => {
const response1 = await got(`${server.url}/hello`, {
decompress: false,
})
t.is(response1.statusCode, 200)
})
})
})

test('redirect with country cookie', async (t) => {
await withSiteBuilder('site-with-country-cookie', async (builder) => {
builder
Expand Down

1 comment on commit f7291d1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Benchmark results

  • Dependency count: 1,312
  • Package size: 278 MB

Please sign in to comment.