1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

fix(runtime/http): expose nextRequest() errors in respondWith() (#10384)

This commit is contained in:
Nayeem Rahman 2021-05-19 13:39:52 +01:00 committed by Bert Belder
parent a1125765ec
commit 218ba031f0
No known key found for this signature in database
GPG key ID: 7A77887B2E2ED461
2 changed files with 68 additions and 3 deletions

View file

@ -272,3 +272,48 @@ unitTest(
await promise;
},
);
unitTest(
{ perms: { net: true } },
async function httpServerNextRequestErrorExposedInResponse() {
const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
const httpConn = Deno.serveHttp(conn);
const event = await httpConn.nextRequest();
assert(event);
// Start polling for the next request before awaiting response.
const nextRequestPromise = httpConn.nextRequest();
const { respondWith } = event;
await assertThrowsAsync(
async () => {
let interval = 0;
await respondWith(
new Response(
new ReadableStream({
start(controller) {
interval = setInterval(() => {
const message = `data: ${Date.now()}\n\n`;
controller.enqueue(new TextEncoder().encode(message));
}, 200);
},
cancel() {
clearInterval(interval);
},
}),
),
);
},
Deno.errors.Http,
"connection closed",
);
// The error from `op_http_request_next` reroutes to `respondWith()`.
assertEquals(await nextRequestPromise, null);
listener.close();
})();
const resp = await fetch("http://127.0.0.1:4501/");
await resp.body!.cancel();
await promise;
},
);

View file

@ -14,6 +14,8 @@
return new HttpConn(rid);
}
const connErrorSymbol = Symbol("connError");
class HttpConn {
#rid = 0;
@ -35,10 +37,16 @@
this.#rid,
);
} catch (error) {
// A connection error seen here would cause disrupted responses to throw
// a generic `BadResource` error. Instead store this error and replace
// those with it.
this[connErrorSymbol] = error;
if (error instanceof errors.BadResource) {
return null;
} else if (error instanceof errors.Interrupted) {
return null;
} else if (error.message.includes("connection closed")) {
return null;
}
throw error;
}
@ -66,7 +74,7 @@
);
const request = fromInnerRequest(innerRequest, "immutable");
const respondWith = createRespondWith(responseSenderRid, this.#rid);
const respondWith = createRespondWith(this, responseSenderRid);
return { request, respondWith };
}
@ -97,7 +105,7 @@
);
}
function createRespondWith(responseSenderRid) {
function createRespondWith(httpConn, responseSenderRid) {
return async function respondWith(resp) {
if (resp instanceof Promise) {
resp = await resp;
@ -145,6 +153,11 @@
innerResp.headerList,
], respBody instanceof Uint8Array ? respBody : null);
} catch (error) {
const connError = httpConn[connErrorSymbol];
if (error instanceof errors.BadResource && connError != null) {
// deno-lint-ignore no-ex-assign
error = new connError.constructor(connError.message);
}
if (respBody !== null && respBody instanceof ReadableStream) {
await respBody.cancel(error);
}
@ -173,6 +186,11 @@
value,
);
} catch (error) {
const connError = httpConn[connErrorSymbol];
if (error instanceof errors.BadResource && connError != null) {
// deno-lint-ignore no-ex-assign
error = new connError.constructor(connError.message);
}
await reader.cancel(error);
throw error;
}
@ -180,7 +198,9 @@
} finally {
// Once all chunks are sent, and the request body is closed, we can
// close the response body.
await Deno.core.opAsync("op_http_response_close", responseBodyRid);
try {
await Deno.core.opAsync("op_http_response_close", responseBodyRid);
} catch { /* pass */ }
}
}
};