mirror of
https://github.com/denoland/deno.git
synced 2024-11-29 16:30:56 -05:00
fix(runtime/http): expose nextRequest() errors in respondWith() (#10384)
This commit is contained in:
parent
a1125765ec
commit
218ba031f0
2 changed files with 68 additions and 3 deletions
|
@ -272,3 +272,48 @@ unitTest(
|
||||||
await promise;
|
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;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
return new HttpConn(rid);
|
return new HttpConn(rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const connErrorSymbol = Symbol("connError");
|
||||||
|
|
||||||
class HttpConn {
|
class HttpConn {
|
||||||
#rid = 0;
|
#rid = 0;
|
||||||
|
|
||||||
|
@ -35,10 +37,16 @@
|
||||||
this.#rid,
|
this.#rid,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} 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) {
|
if (error instanceof errors.BadResource) {
|
||||||
return null;
|
return null;
|
||||||
} else if (error instanceof errors.Interrupted) {
|
} else if (error instanceof errors.Interrupted) {
|
||||||
return null;
|
return null;
|
||||||
|
} else if (error.message.includes("connection closed")) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +74,7 @@
|
||||||
);
|
);
|
||||||
const request = fromInnerRequest(innerRequest, "immutable");
|
const request = fromInnerRequest(innerRequest, "immutable");
|
||||||
|
|
||||||
const respondWith = createRespondWith(responseSenderRid, this.#rid);
|
const respondWith = createRespondWith(this, responseSenderRid);
|
||||||
|
|
||||||
return { request, respondWith };
|
return { request, respondWith };
|
||||||
}
|
}
|
||||||
|
@ -97,7 +105,7 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRespondWith(responseSenderRid) {
|
function createRespondWith(httpConn, responseSenderRid) {
|
||||||
return async function respondWith(resp) {
|
return async function respondWith(resp) {
|
||||||
if (resp instanceof Promise) {
|
if (resp instanceof Promise) {
|
||||||
resp = await resp;
|
resp = await resp;
|
||||||
|
@ -145,6 +153,11 @@
|
||||||
innerResp.headerList,
|
innerResp.headerList,
|
||||||
], respBody instanceof Uint8Array ? respBody : null);
|
], respBody instanceof Uint8Array ? respBody : null);
|
||||||
} catch (error) {
|
} 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) {
|
if (respBody !== null && respBody instanceof ReadableStream) {
|
||||||
await respBody.cancel(error);
|
await respBody.cancel(error);
|
||||||
}
|
}
|
||||||
|
@ -173,6 +186,11 @@
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} 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);
|
await reader.cancel(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -180,7 +198,9 @@
|
||||||
} finally {
|
} finally {
|
||||||
// Once all chunks are sent, and the request body is closed, we can
|
// Once all chunks are sent, and the request body is closed, we can
|
||||||
// close the response body.
|
// 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 */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue