D
Deno

help

How to handle a user abort by Deno.serve

Ppascalav9/13/2023
Hello, I have a question and discovered this Discord channel now. The Deno website has an example at: https://deno.land/manual@v1.36.4/runtime/http_server_apis#inspecting-the-incoming-request There is the note below: Be aware that the req.text() call can fail if the user hangs up the connection before the body is fully received. Make sure to handle this case. However, I have not found a sample how to catch this case. Can Deno detect that the connection has been terminated? I take the example from the Deno site and changed it a bit:
const abortController = new AbortController();
Deno.addSignalListener('SIGINT', () => {
abortController.abort();
});

Deno.serve(
{
port:3000,
signal: abortController.signal
},
async (req) => {
if (req.body) {
try {
console.log('Start');
const body = await req.formData();
for (const key of body.keys()) {
console.log(key);
}
console.log('Never reached, when user abort.');
} catch {
// Will not be triggered on abort.
console.log('Error on await req.formData()');
}
}

return new Response("Hello, World!");
}
);
const abortController = new AbortController();
Deno.addSignalListener('SIGINT', () => {
abortController.abort();
});

Deno.serve(
{
port:3000,
signal: abortController.signal
},
async (req) => {
if (req.body) {
try {
console.log('Start');
const body = await req.formData();
for (const key of body.keys()) {
console.log(key);
}
console.log('Never reached, when user abort.');
} catch {
// Will not be triggered on abort.
console.log('Error on await req.formData()');
}
}

return new Response("Hello, World!");
}
);
By the way, I added the "AbortHandler" for the test. If await req.formData() passes successfully, the server can be terminated with Ctrl + C in the terminal (Linux Mint). If the user cancels the "upload", after the Ctrl + C command is still running a process until I close the terminal. I think that is why Deno writes on the website, you have to catch this case. How to do this?
Deno
HTTP Server APIs | Manual | Deno
Deno currently has three HTTP Server APIs:
DAHDeno AI Helper9/13/2023
According to the documentation, when a user aborts a connection, it can cause methods that read from the request body, such as req.text(), req.json(), req.formData(), req.arrayBuffer(), req.body.getReader().read(), req.body.pipeTo(), etc., to fail. In your code, you are using req.formData(), which is one of the methods that can fail if the user aborts the connection. To handle this, you can use a try-catch block around the req.formData() call, which you have already done in your code. However, it seems like the catch block is not being triggered when the user aborts the connection. This might be due to the way the error is being thrown or propagated. Unfortunately, the documentation does not provide a clear answer on how to handle this specific case. It might be helpful to check the error message or error type when the req.formData() call fails, to see if it provides any clues about the user aborting the connection. Also, it's worth noting that the AbortController and AbortSignal you are using in your code are for aborting the server itself, not for handling user-aborted connections. The AbortSignal is passed to Deno.serve() to allow the server to be aborted, for example, when you press Ctrl + C in the terminal. I hope this helps! If you have any more questions, feel free to ask. 馃
PPartisanI9/13/2023
try using three backticks to format your code:
const abortController = new AbortController();
Deno.addSignalListener('SIGINT', () => {
abortController.abort();
});

Deno.serve(
{
port:3000,
signal: abortController.signal
},
async (req) => {
if (req.body) {
try {
console.log('Start');
const body = await req.formData();
for (const key of body.keys()) {
console.log(key);
}
console.log('Never reached, when user abort.');
} catch {
// Will not be triggered on abort.
console.log('Error on await req.formData()');
}
}

return new Response("Hello, World!");
}
);
const abortController = new AbortController();
Deno.addSignalListener('SIGINT', () => {
abortController.abort();
});

Deno.serve(
{
port:3000,
signal: abortController.signal
},
async (req) => {
if (req.body) {
try {
console.log('Start');
const body = await req.formData();
for (const key of body.keys()) {
console.log(key);
}
console.log('Never reached, when user abort.');
} catch {
// Will not be triggered on abort.
console.log('Error on await req.formData()');
}
}

return new Response("Hello, World!");
}
);
Ppascalav9/13/2023
Formatting of the code has worked. Thanks for the hint. try ... catch doesn't work, because await req.formData() doesn't trigger it. Yes, I know. The AbortSignal abort the server itself. The reason I added it, for example, was that Ctrl + C terminates the server, but then some process is still running in Deno. And only if req.formData() is terminated normally, no process is running in Deno afterwards. So unfortunately the bot does not help here 馃槮
DAHDeno AI Helper9/13/2023
I'm sorry for the confusion. The documentation does not provide a clear way to handle the case where a user aborts the connection during the req.formData() call. It's possible that this is a limitation of the current Deno APIs. However, you might be able to work around this by using a timeout. You could use the AbortSignal.timeout(ms) method to create an AbortSignal that aborts after a certain amount of time. This could help to ensure that the req.formData() call does not hang indefinitely if the user aborts the connection. Here's an example of how you might use AbortSignal.timeout(ms):
const signal = AbortSignal.timeout(5000); // 5 seconds timeout

Deno.serve(
{
port:3000,
signal: signal
},
async (req) => {
if (req.body) {
try {
console.log('Start');
const body = await req.formData();
for (const key of body.keys()) {
console.log(key);
}
console.log('Never reached, when user abort.');
} catch {
// Will not be triggered on abort.
console.log('Error on await req.formData()');
}
}

return new Response("Hello, World!");
}
);
const signal = AbortSignal.timeout(5000); // 5 seconds timeout

Deno.serve(
{
port:3000,
signal: signal
},
async (req) => {
if (req.body) {
try {
console.log('Start');
const body = await req.formData();
for (const key of body.keys()) {
console.log(key);
}
console.log('Never reached, when user abort.');
} catch {
// Will not be triggered on abort.
console.log('Error on await req.formData()');
}
}

return new Response("Hello, World!");
}
);
In this example, if the req.formData() call does not complete within 5 seconds, the AbortSignal will abort, and the req.formData() call should fail and throw an error, which you can catch in the catch block. Please note that this is just a workaround and might not work in all cases. The best solution would be for Deno to provide a way to handle user-aborted connections directly in the API.
Mmmastrac9/14/2023
This might be a bug on our end. I managed to create a repro for it
Mmmastrac9/14/2023
GitHub
Aborted requests do not throw 路 Issue #20502 路 denoland/deno
If a connection hangs up before transmitting the body, request.text() should throw to indicate a failure. Deno.test( { permissions: { net: true } }, async function httpServerAbortedRequestBody() { ...
Mmmastrac9/14/2023
I just PR'd a fix as https://github.com/denoland/deno/pull/20503/. This will be working in the next release of Deno (next week, I believe).
GitHub
fix(ext/http): ensure aborted bodies throw by mmastrac 路 Pull Reque...
Fixes #20502 -- ensure that Hyper errors make it through to JS.

Looking for more? Join the community!

Recommended Posts
API hooks during restart for deno run --watchHello! I thought it would be super fun to further minimize repetitive tasks while manually testing adeno compile -A npm:cowsay not workingHello, I am trying `deno compile -A npm:cowsay` and then following that with `./cowsay Hello World!`compiling issue with import { WebSocketServer } from "npm:ws@^8.14.1"Using this impot in a "deno run --unstable [etc...]" env works flawless / as intended but trying to Is there any way to get Deno to not statically analyze import()?I understand that statically analyzing and fetching dynamic import() calls is a good thing for perfoUsing Deno in Frontend.Hey, I was wandering if it is possible to use Deno for Vanilla Frontend development. I dont want to Excluding certain directories when using "walk"Hello, I'm writing a script that should crawl through a directory and search for a given text insideSyntax Error: Unexpected token 'export'Hello, We are using Supabase Deno Edge Functions and are getting this error while trying to import aDeno Deploy QuestionsHey there. We are using Supabase to run edge functions, which in turn built their feature on top of Bunle OAK static assets to the compiled binaryHi, I am currently switching from NodeJS to Deno, and so far so good! One of the reasons for this sThe requested module does not provide an export named 'default'I'm trying to export the following interface: ```import User from '../models/User.ts'; export defaI want to contribute to deno without disturbing my libs in linux . Is there a way?**Noob alert** I have been trying to contribute to deno . So i followed these instructions https://dTimezones in Dates?Hi everyone, what's the right way to create a `Date` with a specific timezone?Resolving Class constructor errorHello, I am currently trying to port a small test api from NodeJS to Deno. This code uses only NPM pKV list is returning an empty iterator, but data is there and gettable via getHello. To simplify things, I am setting a key of `["accounts", account.name]` but when I try to get Read permission & NPM importsHello, I'm using `--allow-read=.` to allow a script to read files in the current directory. The scriWebSocket connections killing setInterval()Hey Deno friends, it appears that websocket connections are somehow stopping the execution of setIntjsx in jsAll of my files containing JSX are in `.js` files. Is there an easy fix for this when running?http errorI need help because I have a service that has more than 1 million requests per day and for about 7 d