Skip to content

Question: Any way to safely get a ReadWriteCloser compatible with x/net/websocket? #282

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

Closed
prasannavl opened this issue Sep 7, 2017 · 4 comments

Comments

@prasannavl
Copy link

prasannavl commented Sep 7, 2017

I'm trying to port https://github.com/rsms/gotalk to use gorilla websockets instead of x/net/websocket. However, it uses a common underlying handler for both the raw TCP stream, and websockets stream. The websockets one uses /x/net/websocket where it upgrades the connection, and the passes it on to the raw TCP handler, that handles things from there.

To be able to do this, x/net/websocket is pretty trivial. All it does is exposes the Conn after the upgrade that can be sent as a ReadWriteCloser. I'm wondering if it's possible to achieve this using Gorilla websockets, while still retaining the higher level features like compression that gorilla socket provides. From what little I looked under the hood, it only seems to have the raw underlying connection conn which cannot be passed since it will include the websocket framing, and reader and writer which are exposed through NextReader and NextWriter which can change. Any way to do this without writing a complicated abstraction layer on top of NextReader/NextWriter?

@garyburd
Copy link
Contributor

garyburd commented Sep 7, 2017

The x/net/websocket package implements the net.Conn interface using messages. You can do the same. Here's an untested sketch on how to do this. It's not very complicated.

type rwc struct {
    r io.Reader
    c *websocket.Conn
}

func (c *rwc) Write(p []byte) (int, error) {
    err := c.c.WriteMessage(websocket.BinaryMessage, p)
    if err != nil {
        return 0, err
    }
    return len(p), nil
}

func (c *rwc) Read(p []byte) (int, error) {
    for {
        if c.r == nil {
            // Advance to next message.
            var err error
            _, c.r, err = c.c.NextReader()
            if err != nil {
                return 0, err
            }
        }
        n, err := c.r.Read(p)
        if err == io.EOF {
            // At end of message.
            c.r = nil
            if n > 0 {
                return n, nil
            } else {
                // No data read, continue to next message.
                continue
            }
        }
        return n, err
    }
}

func (c *rwc) Close(() error {
    return c.c.Close()
}

@prasannavl
Copy link
Author

@garyburd , thank you so for the quick sketch. I expected it to be more complex. This is very nice. Switched out my implementations, and works perfectly so far.

Closing this. But, just out of curiosity, any specific reason, why this can't be a part of websockets itself?
Since the read, write interfaces are pretty standard - or perhaps, you feel, it's just not worth maintaining this in the package itself, considering how simple it is?

@garyburd
Copy link
Contributor

garyburd commented Sep 7, 2017

The io.Reader and io.Writer interfaces are standard for byte streams. A websocket connection is a stream of messages, not bytes.

@prasannavl
Copy link
Author

prasannavl commented Sep 7, 2017 via email

@gorilla gorilla locked and limited conversation to collaborators Feb 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants