Skip to content

Commit

Permalink
fix(ext/flash): Correctly handle errors for chunked responses (denola…
Browse files Browse the repository at this point in the history
…nd#17303)

The leading cause of the problem was that `handleResponse` has
`tryRespondChunked` passed as an argument, which in turn is implemented
as a call to `core.ops.op_try_flash_respond_chuncked`, that throws in
the repro code.

`handleResponse` was not handled correctly, as it not returned any
value, and had no `catch` attached to it.
It also effectively was never correctly handled inside two other blocks
with `resp.then` and `PromisePrototypeCatch(PromisePrototypeThen(resp,
"..."))` as well, as it just short-circuited the promise with an empty
resolve, instead of relying on the last `(async () => {})` block.

This change makes `handleResponse` return a correct value and attach
`onError` handler to the "non-thenable" variant of response handling
code.
  • Loading branch information
kamilogorek committed Jan 14, 2023
1 parent ae2981d commit 1d7203c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 16 deletions.
46 changes: 46 additions & 0 deletions cli/tests/unit/flash_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
assert,
assertEquals,
assertRejects,
assertStringIncludes,
assertThrows,
Deferred,
deferred,
Expand Down Expand Up @@ -495,6 +496,51 @@ Deno.test(
},
);

// https://github.com/denoland/deno/issues/17291
Deno.test(
{ permissions: { net: true } },
async function httpServerIncorrectChunkedResponse() {
const ac = new AbortController();
const listeningPromise = deferred();
const errorPromise = deferred();
const server = Deno.serve({
handler: () => {
const body = new ReadableStream({
start(controller) {
// Non-encoded string is not a valid readable chunk.
controller.enqueue("wat");
},
});
return new Response(body);
},
port: 4501,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: (err) => {
const errResp = new Response(
`Internal server error: ${(err as Error).message}`,
{ status: 500 },
);
ac.abort();
errorPromise.resolve(errResp);
return errResp;
},
});

await listeningPromise;

const resp = await fetch("https://127.0.0.1:4501/");
// Incorrectly implemented reader ReadableStream should reject.
await assertRejects(() => resp.body!.getReader().read());

const err = await errorPromise as Response;
assertStringIncludes(await err.text(), "Expected ArrayBufferView");

ac.abort();
await server;
},
);

Deno.test(
{ permissions: { net: true } },
async function httpServerCorrectLengthForUnicodeString() {
Expand Down
30 changes: 14 additions & 16 deletions ext/flash/01_http.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@
);
}

(async () => {
return (async () => {
if (!ws) {
if (hasBody && body[_state] !== "closed") {
// TODO(@littledivy): Optimize by draining in a single op.
Expand Down Expand Up @@ -590,7 +590,6 @@
),
onError,
);
continue;
} else if (typeof resp?.then === "function") {
resp.then((resp) =>
handleResponse(
Expand All @@ -606,24 +605,23 @@
tryRespondChunked,
)
).catch(onError);
continue;
} else {
handleResponse(
req,
resp,
body,
hasBody,
method,
serverId,
i,
respondFast,
respondChunked,
tryRespondChunked,
).catch(onError);
}
} catch (e) {
resp = await onError(e);
}

handleResponse(
req,
resp,
body,
hasBody,
method,
serverId,
i,
respondFast,
respondChunked,
tryRespondChunked,
);
}

offset += tokens;
Expand Down

0 comments on commit 1d7203c

Please sign in to comment.