Open nounder opened 1 year ago
I went ahead with handling Deno.RequestEvent
directly like so:
const server = Deno.listen({ port: 8080 })
// From:
// https://deno.com/manual@v1.33.2/runtime/http_server_apis#responding-with-a-response
async function handle(conn: Deno.Conn) {
const httpConn = Deno.serveHttp(conn)
for await (const requestEvent of httpConn) {
try {
await requestEvent.respondWith(
await RootRouter.fetch(requestEvent.request)
)
} catch (err) {
console.warn(err)
}
}
}
for await (const conn of server) {
handle(conn)
}
Although there is try/catch around respondWith
, the server still crashes:
error: Uncaught Http: connection closed before message completed
await requestEvent.respondWith(
^
at Object.respondWith (ext:deno_http/01_http.js:328:21)
at eventLoopTick (ext:core/01_core.js:188:13)
at async handle (file:///MyOwnCode/main.ts:421:9)
Here's standalone example:
import * as shed from "https://raw.githubusercontent.com/worker-tools/shed/master/index.ts"
export const RootRouter = new shed.WorkerRouter()
RootRouter.get("/", () => {
const stream = new TransformStream()
const writer = stream.writable.getWriter()
setInterval(() => {
writer.write(new TextEncoder().encode("ping\n"))
}, 100)
return new shed.StreamResponse(stream.readable, {
headers: {
"Content-Type": "text/event-stream",
},
})
})
const server = Deno.listen({ port: 8080 })
// From:
// https://deno.com/manual@v1.33.2/runtime/http_server_apis#responding-with-a-response
async function handle(conn: Deno.Conn) {
const httpConn = Deno.serveHttp(conn)
for await (const requestEvent of httpConn) {
try {
await requestEvent.respondWith(RootRouter.fetch(requestEvent.request))
} catch (err) {
console.log("Connection loop error")
console.warn(err)
break
}
}
}
for await (const conn of server) {
handle(conn)
}
Now go to localhost:8080
and refresh a page. Process will crash:
error: Uncaught Http: connection closed before message completed
await requestEvent.respondWith(RootRouter.fetch(requestEvent.request))
^
at Object.respondWith (ext:deno_http/01_http.js:328:21)
at eventLoopTick (ext:core/01_core.js:188:13)
at async handle (file:///[...]/shed_break.ts:29:7)
Using server_sent_event.ts from std solves the issue. See https://github.com/denoland/deno/issues/19143#issuecomment-1549141025 for more.
One can filter SSE closes with following code:
async function handle(conn: Deno.Conn) {
const httpConn = Deno.serveHttp(conn)
for await (const requestEvent of httpConn) {
const responsePromise = RootRouter.fetch(requestEvent.request)
try {
await requestEvent.respondWith(responsePromise)
} catch (err) {
const res = await responsePromise
if (
res.headers.get("content-type") === "text/event-stream" &&
err.message === "connection closed before message completed"
) {
continue
}
throw err
}
}
}
With Server-sent endpoints it is often the case that connection is closed before Deno flushes body stream. When that happens, following error is thrown:
I tried to try/catch handler and listen to router 'error' handler but it looks like the error is thrown outside the context of the router.
Is it somehow possible to catch this error and conditionally silence it?