mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-07 21:07:46 +00:00
[cli][dev] handle no response from edge functions (#8095)
When an edge function has no response during `vc dev`, we were seeing an unhelpful error message: > The event listener did not respond. Now, we'll see a much more specific error message: > Unhandled rejection: Edge Function "api/edge-no-response.ts" did not return a response. > Error! Failed to complete request to /api/edge-no-response: Error: socket hang up
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
export const config = {
|
||||
runtime: 'experimental-edge',
|
||||
};
|
||||
|
||||
export default async function edge(request, event) {
|
||||
// nothing returned
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function serverless(request, response) {
|
||||
return response.send('hello from a serverless function');
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export const config = {
|
||||
runtime: 'experimental-edge',
|
||||
};
|
||||
|
||||
export default async function edge(request, event) {
|
||||
// no response
|
||||
}
|
||||
@@ -56,6 +56,31 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test('[vercel dev] throws an error when an edge function has no response', async () => {
|
||||
const dir = fixture('edge-function-error');
|
||||
const { dev, port, readyResolver } = await testFixture(dir);
|
||||
|
||||
try {
|
||||
await readyResolver;
|
||||
|
||||
let res = await fetch(`http://localhost:${port}/api/edge-no-response`);
|
||||
validateResponseHeaders(res);
|
||||
|
||||
const { stdout, stderr } = await dev.kill('SIGTERM');
|
||||
|
||||
expect(await res.status).toBe(500);
|
||||
expect(await res.text()).toMatch('FUNCTION_INVOCATION_FAILED');
|
||||
expect(stdout).toMatch(
|
||||
/Unhandled rejection: Edge Function "api\/edge-no-response.js" did not return a response./g
|
||||
);
|
||||
expect(stderr).toMatch(
|
||||
/Failed to complete request to \/api\/edge-no-response: Error: socket hang up/g
|
||||
);
|
||||
} finally {
|
||||
await dev.kill('SIGTERM');
|
||||
}
|
||||
});
|
||||
|
||||
test('[vercel dev] should support edge functions returning intentional 500 responses', async () => {
|
||||
const dir = fixture('edge-function');
|
||||
const { dev, port, readyResolver } = await testFixture(dir);
|
||||
|
||||
@@ -442,6 +442,17 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Middleware that has no response',
|
||||
testFixtureStdio('middleware-no-response', async (testPath: any) => {
|
||||
await testPath(
|
||||
500,
|
||||
'/api/hello',
|
||||
'A server error has occurred\n\nEDGE_FUNCTION_INVOCATION_FAILED'
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Middleware that does basic rewrite',
|
||||
testFixtureStdio('middleware-rewrite', async (testPath: any) => {
|
||||
|
||||
@@ -148,21 +148,26 @@ async function serializeRequest(message: IncomingMessage) {
|
||||
});
|
||||
}
|
||||
|
||||
async function compileUserCode(entrypoint: string) {
|
||||
async function compileUserCode(
|
||||
entrypointPath: string,
|
||||
entrypointLabel: string
|
||||
) {
|
||||
try {
|
||||
const result = await esbuild.build({
|
||||
platform: 'node',
|
||||
target: 'node14',
|
||||
sourcemap: 'inline',
|
||||
bundle: true,
|
||||
entryPoints: [entrypoint],
|
||||
entryPoints: [entrypointPath],
|
||||
write: false, // operate in memory
|
||||
format: 'cjs',
|
||||
});
|
||||
|
||||
const compiledFile = result.outputFiles?.[0];
|
||||
if (!compiledFile) {
|
||||
throw new Error(`Compilation of ${entrypoint} produced no output files.`);
|
||||
throw new Error(
|
||||
`Compilation of ${entrypointLabel} produced no output files.`
|
||||
);
|
||||
}
|
||||
|
||||
const userCode = new TextDecoder().decode(compiledFile.contents);
|
||||
@@ -198,6 +203,10 @@ async function compileUserCode(entrypoint: string) {
|
||||
|
||||
let response = await edgeHandler(event.request, event);
|
||||
|
||||
if (!response) {
|
||||
throw new Error('Edge Function "${entrypointLabel}" did not return a response.');
|
||||
}
|
||||
|
||||
return event.respondWith(response);
|
||||
} catch (error) {
|
||||
// we can't easily show a meaningful stack trace
|
||||
@@ -252,9 +261,10 @@ async function createEdgeRuntime(userCode: string | undefined) {
|
||||
}
|
||||
|
||||
async function createEdgeEventHandler(
|
||||
entrypoint: string
|
||||
entrypointPath: string,
|
||||
entrypointLabel: string
|
||||
): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>> {
|
||||
const userCode = await compileUserCode(entrypoint);
|
||||
const userCode = await compileUserCode(entrypointPath, entrypointLabel);
|
||||
const server = await createEdgeRuntime(userCode);
|
||||
|
||||
return async function (request: IncomingMessage) {
|
||||
@@ -317,17 +327,17 @@ async function createEventHandler(
|
||||
config: Config,
|
||||
options: { shouldAddHelpers: boolean }
|
||||
): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>> {
|
||||
const entryPointPath = join(process.cwd(), entrypoint!);
|
||||
const runtime = parseRuntime(entrypoint, entryPointPath);
|
||||
const entrypointPath = join(process.cwd(), entrypoint!);
|
||||
const runtime = parseRuntime(entrypoint, entrypointPath);
|
||||
|
||||
// `middleware.js`/`middleware.ts` file is always run as
|
||||
// an Edge Function, otherwise needs to be opted-in via
|
||||
// `export const config = { runtime: 'experimental-edge' }`
|
||||
if (config.middleware === true || runtime === 'experimental-edge') {
|
||||
return createEdgeEventHandler(entryPointPath);
|
||||
return createEdgeEventHandler(entrypointPath, entrypoint);
|
||||
}
|
||||
|
||||
return createServerlessEventHandler(entryPointPath, options);
|
||||
return createServerlessEventHandler(entrypointPath, options);
|
||||
}
|
||||
|
||||
let handleEvent: (request: IncomingMessage) => Promise<VercelProxyResponse>;
|
||||
|
||||
Reference in New Issue
Block a user