Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Eelis opened this issue Jan 27, 2019 · 10 comments

Comments

@Eelis
Copy link

Eelis commented Jan 27, 2019

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.

@ghost
Copy link

ghost commented Jan 27, 2019

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
Copy link
Author

Eelis commented Jan 28, 2019

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
Copy link

ghost commented Jan 28, 2019

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.

@Eelis
Copy link
Author

Eelis commented Feb 13, 2019

@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.

@garyburd
Copy link
Contributor

I closed the issue because I thought the issue was resolved. @stevenscott89 responded to your points and there was no comment from you in two weeks.

The feature is probably not difficult to implement, but it will need to wait until a new project maintainer is found. I'll reopen the issue so it's on the radar for the new maintainer.

@aspotashev
Copy link

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

@practicode-org
Copy link

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
Copy link

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
Copy link
Contributor

FMLS commented Mar 21, 2022

watching this issue

@ddbxyrj
Copy link

ddbxyrj commented Jun 7, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

9 participants