Skip to content

Fix graceful shutdown hanging and killing in-flight requests - #713

Open
kksingh000 wants to merge 1 commit into
oakserver:mainfrom
kksingh000:fix/graceful-shutdown-abort
Open

kksingh000 wants to merge 1 commit into
oakserver:mainfrom
kksingh000:fix/graceful-shutdown-abort

Conversation

@kksingh000

Copy link
Copy Markdown

When an Application was listening with an AbortSignal and that signal was aborted while requests were in flight, two problems occurred:

  1. listen() never resolved. Server#listen() in http_server_native.ts never closed the ReadableStream's controller when the underlying Deno.serve() instance shut down, so the for await loop consuming that stream in Application#listen() hung forever even after all in-flight requests had been handled.

  2. In-flight requests were killed instead of completing. Server#listen() passed signal directly into Deno.serve()'s own options, which causes Deno's engine to hard-abort in-flight connections immediately on signal abort. It also registered its own abort listener that called this.close() (and therefore httpServer.shutdown()) straight away, racing Application#listen()'s own careful tracking of in-flight requests (state.handling), which already defers calling Server#close() until those requests have drained.

Fix both by:

  • Closing the stream's controller once Deno.serve()'s own .finished promise resolves, so the consuming for await loop (and therefore the top-level listen() promise) terminates correctly.
  • No longer passing signal into Deno.serve()'s own options, and removing Server#listen()'s own abort-triggered call to close(). Application#listen() already owns the correct abort-then-drain- then-close sequencing; Server no longer races it.

Fixes #611

Claude-Session: https://claude.ai/code/session_0126cmS4qpPbYhiSmnHo3cUo

When an Application was listening with an AbortSignal and that signal
was aborted while requests were in flight, two problems occurred:

1. listen() never resolved. Server#listen() in http_server_native.ts
   never closed the ReadableStream's controller when the underlying
   Deno.serve() instance shut down, so the `for await` loop consuming
   that stream in Application#listen() hung forever even after all
   in-flight requests had been handled.

2. In-flight requests were killed instead of completing. Server#listen()
   passed `signal` directly into Deno.serve()'s own options, which
   causes Deno's engine to hard-abort in-flight connections immediately
   on signal abort. It also registered its own `abort` listener that
   called `this.close()` (and therefore `httpServer.shutdown()`)
   straight away, racing Application#listen()'s own careful tracking of
   in-flight requests (`state.handling`), which already defers calling
   `Server#close()` until those requests have drained.

Fix both by:
- Closing the stream's controller once Deno.serve()'s own `.finished`
  promise resolves, so the consuming `for await` loop (and therefore
  the top-level `listen()` promise) terminates correctly.
- No longer passing `signal` into Deno.serve()'s own options, and
  removing Server#listen()'s own abort-triggered call to `close()`.
  Application#listen() already owns the correct abort-then-drain-
  then-close sequencing; Server no longer races it.

Fixes oakserver#611

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126cmS4qpPbYhiSmnHo3cUo

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No graceful shutdown behavior

2 participants