Skip to content

No graceful shutdown behavior #611

@kromeyer

Description

@kromeyer

Hi everyone,

when Oak is stopped via AbortController and there still incoming requests, I get strange responses. Here is a litte test setup:

Environment

  • macOS 13.4
  • deno 1.34.0 (release, x86_64-apple-darwin)
  • v8 11.5.150.1
  • typescript 5.0.4
  • oak 12.5.0

Setup

// oak_server.ts
import {Application} from "https://deno.land/x/oak@v12.5.0/application.ts";
import {delay} from "https://deno.land/std@0.190.0/async/delay.ts";
import {Status} from "https://deno.land/std@0.190.0/http/http_status.ts";

let aborted = false;
const abortController = new AbortController();
const application = new Application();
application.use(async (ctx) => {
    if (!aborted) {
        aborted = true;
        console.log("Server will be stopped");
        abortController.abort();
    }
    await delay(3000);
    ctx.response.status = Status.OK;
});
console.log("Server will be started");
const serverPromise = application.listen({port: 9000, signal: abortController.signal});
console.log("Server started");
await serverPromise;
console.log("Server stopped");
// requests_for_tests.ts
import {delay} from "https://deno.land/std@0.190.0/async/delay.ts";
import {exec} from "https://deno.land/x/exec/mod.ts";

function startStopwatch() {
    const start = Date.now();
    return () => Date.now() - start;
}

function doTestFetch(label: string) {
    console.log(`${label} request started`)
    const stopStopwatch = startStopwatch();
    return fetch("http://localhost:9000/")
        .then(response => {
            console.log(`${label} request status: ${response.status}`);
            return response.body?.cancel();
        })
        .catch(error => {
            console.log(`${label} request failed: ${error}`);
        })
        .finally(() => {
            console.log(`${label} request time: ${stopStopwatch()}ms`);
        });
}

const execPromise = exec("deno run --allow-net test/oak_server.ts")
await delay(3000);
const firstRequestPromise = doTestFetch("(1)");
await delay(100);
const secondRequestPromise = doTestFetch("(2)");
await Promise.allSettled([execPromise, firstRequestPromise, secondRequestPromise]);

Output

Server will be started
Server started
(1) request started
Server will be stopped
(2) request started
(2) request status: 404
(2) request time: 7ms
(1) request failed: TypeError: error sending request for url (http://localhost:9000/): connection closed before message completed
(1) request time: 113ms
Server stopped

Expected behavior

  • Request (1) should be cleanly handled and get a 200 response after the delay
  • Request (2) should be fail with a connection refused or a HTTP 503 response as fast as possible

Regards

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds investigationSomething that needs to be looked at

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions