Websocket: Graceful shutdown of the websocket connection.

Created on 5 Nov 2018  Â·  7Comments  Â·  Source: gorilla/websocket

It seems like to achieve a "normal" shutdown of a websocket connection, one needs to do a dance like:

  1. Send Close message
  2. Wait for receiving a Close message
  3. Actually close the connection

The Close method of today closes the connection immediately, without warning or graceful shutdown. Would it be desirable to have a Shutdown method like golang's http servers, which does the dance explained above for the user?

help wanted waiting on new maintainer

Most helpful comment

I think this is desirable to have, as well as in-built support for checking liveness using ping/pong and deadlines. Most applications need these features.

The current API mostly handles the wire format. The only thing the package does above the wire format is respond to ping and respond to close. This low-level API provides flexibility for the applications that need it (like the one I work on), but most don't need this flexibility.

This package can benefit from a high-level API that wraps up all these things that applications need: shutdown, checking liveness, concurrent sends, ...

All 7 comments

I think this is desirable to have, as well as in-built support for checking liveness using ping/pong and deadlines. Most applications need these features.

The current API mostly handles the wire format. The only thing the package does above the wire format is respond to ping and respond to close. This low-level API provides flexibility for the applications that need it (like the one I work on), but most don't need this flexibility.

This package can benefit from a high-level API that wraps up all these things that applications need: shutdown, checking liveness, concurrent sends, ...

@garyburd Can you please assign this issue to me. I would like to work on it.

@ankur0493 Please post a proposed API and overview of the implementation for discussion.

I tried to assign you to this issue, but Github will not let me do it. Please let me know what I need to do.

(Can’t assign to non-maintainers of a repo. I think it’s fair just note
that @ankur0493 has it & follow up!)
On Sun, Dec 9, 2018 at 7:44 PM Gary Burd notifications@github.com wrote:

@ankur0493 https://github.com/ankur0493 Please post a proposed API and
overview of the implementation for discussion.

I tried to assign you to this issue, but Github will not let me do it.
Please let me know what I need to do.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/gorilla/websocket/issues/448#issuecomment-445659075,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AABIcJ-3eUsrZCkdzplRzK_dqmoxrQhwks5u3dh-gaJpZM4YOjKk
.

Thanks @garyburd. Will soon share the API details.

This feature is a helper method. It's possible for applications to implement the RFC today. If an application does not initiate the closing handshake, then the application probably implements the RFC without doing anything special.

Based on discussion in #487, here's my summary of the design:

To avoid concurrent reads on the connection, the following two cases must be handled differently:

  • Shutdown is executed from the reading goroutine
  • Shutdown in executed from some other goroutine.

The common code for both cases starts with:

err := c.WriteControlMessage(CloseMessage,FormatCloseMessage(code, msg))
if err != nil && err != ErrCloseSent {
      // If close message could not be sent, then close without the handshake.
      return c.Close()
}

The remaining code for the reading goroutine case is:

 // To prevent ping, pong and close handlers from setting the read deadline, set these handlers to the default.
c.SetPingHandler(nil)
c.SetPongHandler(nil)
c.SetCloseHandler(nil)
c.SetReadDeadline(timeout)
for {
    if _, _, err := c.NextReader(); err != nil {
        break
    }
}
return c.Close()

The remaining code for shutdown from another goroutine is:

    select {
    case <- c.readDone:
    case <- time.After(timeout):
    }
    return c.Close()

where c.readDone is a channel that's closed when c.readErr is changed from nil to a non-nil value.

For someone who wants this now, I've implemented it in nhooyr.io/[email protected]

See discussion in https://github.com/nhooyr/websocket/pull/160

Was this page helpful?
0 / 5 - 0 ratings

Related issues

silbinarywolf picture silbinarywolf  Â·  7Comments

bcashier picture bcashier  Â·  8Comments

fbens picture fbens  Â·  3Comments

joshdvir picture joshdvir  Â·  50Comments

deanm picture deanm  Â·  15Comments