Skip to content

Add softer ReadMessage timeout mechanism that only cancels the operation and doesn't corrupt the entire connection #474

@Eelis

Description

@Eelis

Currently, as far as I can see, the only mechanism for getting a timeout on a ReadMessage call is by setting a read deadline with SetReadDeadline.

This works, but if a read times out due to a read deadline, the whole connection is corrupted and cannot be read from anymore(!).

This is a surprising and extremely severe result that makes the mechanism unsuitable when all you really want is to just try receiving for a while, and if no message was received, then go do other stuff, and re-try receiving on the same (intact) connection later. As far as I can tell (please correct me if I'm wrong), there is currently no way to do this, which is unfortunate.

Activity

ghost

ghost commented on Jan 27, 2019

@ghost

That's correct. The connection methods use local variables to maintain state as is common with Go code. That state is lost of a connection method returns because of an error on the underlying connection. The connection is no longer useable after the state is lost. The connection methods could be written to be restorable, but that will take some effort.

The solution is to use a goroutine to go off and do other work. Because the application must read the connection to process control messages (documentation), it's not a good idea to go off and do other stuff for a long time.

Eelis

Eelis commented on Jan 28, 2019

@Eelis
Author

Yeah, in my use case the "other stuff" that I wanted to periodically do between ReadMessage calls was just a quick, non-blocking check to see if the component that consumes the messages is still interested in consuming more of them.

As an awkward workaround, I can have a separate goroutine that does the periodic checking and closes the websocket connection when the component isn't interested in consuming additional messages anymore, and then make sure I handle the resulting expected read errors specially. It would just be nicer if this awkward workaround with an additional goroutine and error handling wasn't necessary. :)

ghost

ghost commented on Jan 28, 2019

@ghost

When the component is no longer interested in receiving messages, the application should send a close message to the peer. The peer will echo the message back and that message will be returned as an error from the websocket connection's read API. If you use websocket.CloseNormalClosure or some other expected error code, no special error handling should be required.

The application should force close the connection after a timeout to handle the case where the peer does not correctly echo the close message back to the application.

ghost closed this as completedon Feb 13, 2019
Eelis

Eelis commented on Feb 13, 2019

@Eelis
Author

@garyburd: Hmm, is it normal for you to close tickets without stating a reason? :) In any case, is it fair to conclude from stevenscott89's comment (quoted below) that the reason for closing the ticket is that not killing the connection on read timeouts is too hard in Go?

The connection methods use local variables to maintain state as is common with Go code. That state is lost of a connection method returns because of an error on the underlying connection. The connection is no longer useable after the state is lost. The connection methods could be written to be restorable, but that will take some effort.

ghost reopened this on Feb 14, 2019
aspotashev

aspotashev commented on Aug 16, 2019

@aspotashev

This would be easy to implement if we had this feature -> #481

practicode-org

practicode-org commented on Aug 20, 2021

@practicode-org

Hi! If I want non blocking ReadMessage (in order to be able to finish a "reader" goroutine with an external signal), how can I achieve that? I also don't want to close current connection, because it's supposed to be long-living. Thanks!

robertfarnum

robertfarnum commented on Jan 13, 2022

@robertfarnum

This is indeed a serious issue with the gorilla embedded net.Conn implementation and is non compliant with non blocking network reads from within a goroutine that is checking for <-ctx.Done() or other timeout conditions periodically. Then again issue #282 seems to indicate by no means is the gorilla websocket implementation attempting to be net.Conn compliant. I also would like to seem updates to this module to resolve these issues.

FMLS

FMLS commented on Mar 21, 2022

@FMLS
Contributor

watching this issue

ddbxyrj

ddbxyrj commented on Jun 7, 2022

@ddbxyrj

I want to use two set of processing methods between con life cycle statuses. I kow I can be done by use chan, and alway let on fuction (goroutine) to rev message. But for more good efficiency, maybe we can add this.
Maybe we want to cancle read operation, we can use this. I test tcp socket, and UnderlyingConn(), on the level of net.con SetReadDeadline can renew con error.

shaunco

shaunco commented on Jun 3, 2025

@shaunco

After so many years of non-blocking I/O for everything net.Conn based, this was somewhat shocking to run into in this package. I see there are clear comments in the code on SetReadDeadline, but this feels worthy of proper documentation in the readme or somewhere else.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @elithrar@Eelis@aspotashev@shaunco@coreydaley

        Issue actions

          Add softer ReadMessage timeout mechanism that only cancels the operation and doesn't corrupt the entire connection · Issue #474 · gorilla/websocket