Skip to content

Abort sequence issue #686

Open
Open
@nomocas

Description

@nomocas

Hello All,

Env:
OSX: Darwin Kernel Version 23.6.0 arm64 (apple silicon)
Deno: v2.1.3
Oak: v17.3.1

There is an issue with the way the abort sequence is managed. If we take the example from the github docs, the promise returned by the app.listen is never resolved in any case, and when the abort is triggered right before the await listenPromise (as in the example), the close event is never dispatched (we see in the logs results that the abort event is emitted before the server.listen promise is resolved).

import { Application } from '@oak/oak/application'

const app = new Application()

const controller = new AbortController()
const { signal } = controller

// Dummy middleware or it throws
app.use((_, next) => {
	console.log('Middleware 1')
	return next()
})

const listenPromise = app.listen({ port: 8000, signal })

// dispatched after the server.listen promise resolves
app.addEventListener('listen', () => {
	console.log('Server is listening')
})

// Never dispatched! (it should have been dispatched after the abort controller is aborted but nothing happened)
app.addEventListener('close', () => {
	console.log('Server closed (event)')
})

controller.signal.addEventListener('abort', () => {
	console.log('Abort!')
})

controller.abort()

// On abort: Listen should stop listening for requests and the promise should resolve...
await listenPromise

// Never reached!
console.log('Server closed (promise resolved)')

Logs result:

Abort!
Server is listening

So nothing happens and the process is stuck.

If we add a setTimeout around the controller.abort():

import { Application } from '@oak/oak/application'

const app = new Application()

const controller = new AbortController()
const { signal } = controller

// Dummy middleware or it throws
app.use((_, next) => {
	console.log('Middleware 1')
	return next()
})

const listenPromise = app.listen({ port: 8000, signal })

// dispatched after the server.listen promise resolves
app.addEventListener('listen', () => {
	console.log('Server is listening')
})

// dispatched after the abort controller is aborted
app.addEventListener('close', () => {
	console.log('Server closed (event)')
})

// never dispatched (so no underlying error)
app.addEventListener('error', (error) => {
	console.error('Server error (event)', error)
})

controller.signal.addEventListener('abort', () => {
	console.log('Abort!')
})

setTimeout(() => {
	controller.abort()
}, 1000)

// On abort: Listen should stop listening for requests and the promise should resolve...
await listenPromise

// Never reached!
console.log('Server closed (promise resolved)')

Logs results:

Server is listening
Abort!
Server closed (event)
error: Top-level await promise never resolved
await listenPromise
^

Again, the listenPromise is never resolved and it throws an error.

When I check the @oak/oak/application/Application.listen code, it seems to be stuck in the loop (line 852) or the subsequent await:

      for await (const request of server) {
        this.#handleRequest(request, secure, state);
      }
      await Promise.all(state.handling);

Any clue ?

Thank you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions