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

Support websockets #9

Closed
magiconair opened this issue Nov 1, 2015 · 9 comments
Closed

Support websockets #9

magiconair opened this issue Nov 1, 2015 · 9 comments
Milestone

Comments

@magiconair
Copy link
Contributor

Some people have been asking about websocket support for fabio. I think it shouldn't be too hard to add but I need to see when I can get to it. Keeping this as a placeholder so that I don't forget.

@magiconair
Copy link
Contributor Author

Added support for transparent websocket proxying. Works for my simple client but could use some real-world testing.

magiconair added a commit that referenced this issue Nov 11, 2015
magiconair added a commit that referenced this issue Nov 11, 2015
* Add websocket support to demo/server
* Add simple websocket client in demo/wsclient
@magiconair
Copy link
Contributor Author

I've added experimental websocket support. See documentation https://github.com/eBay/fabio#websockets.

I call it experimental since I don't have an in-house use case to test this beyond a simple test client which is included in the code base. Please test and file issues if something is broken.

@emicklei
Copy link

Thank you adding suppport for websockets. Before I stress test the current state, I like to share with you a snippet I got from Brad Fritzpatrick (see go-nuts link) which it more compact and appearantly does not require the websockets package.

// https://groups.google.com/forum/#!topic/golang-nuts/KBx9pDlvFOc
func websocketProxy(target string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        d, err := net.Dial("tcp", target)
        if err != nil {
            http.Error(w, "Error contacting backend server.", 500)
            log.Printf("Error dialing websocket backend %s: %v", target, err)
            return
        }
        hj, ok := w.(http.Hijacker)
        if !ok {
            http.Error(w, "Not a hijacker?", 500)
            return
        }
        nc, _, err := hj.Hijack()
        if err != nil {
            log.Printf("Hijack error: %v", err)
            return
        }
        defer nc.Close()
        defer d.Close()

        err = r.Write(d)
        if err != nil {
            log.Printf("Error copying request to target: %v", err)
            return
        }

        errc := make(chan error, 2)
        cp := func(dst io.Writer, src io.Reader) {
            _, err := io.Copy(dst, src)
            errc <- err
        }
        go cp(d, nc)
        go cp(nc, d)
        <-errc
    })
}

@magiconair
Copy link
Contributor Author

The only caveat I can think of is that this is no longer an application level gateway. With this code you don't know whether you're actually forwarding websocket data since you're forwarding whatever goes over the wire. The current implementation will forward the messages only having properly terminated the WS messages. That has benefits and downsides. Need to think about it.

@magiconair
Copy link
Contributor Author

Tested the code above and it works. I'll do the error checking slightly different to make sure you don't end up with a dangling TCP connection. The benefits are one dependency less and independence of the websocket protocol version. The downside is that after the the initial handshake a malicious client can throw whatever traffic at the server. Opinions?

@magiconair magiconair reopened this Nov 16, 2015
@magiconair
Copy link
Contributor Author

In the process I've learned about Request.Write 😃

@magiconair
Copy link
Contributor Author

I've added the raw websocket handler for testing but left it disabled. See proxy/proxy.go on how to enable it.

I'm still leaning towards the filtered WS handler unless there is a performance and/or compatibility issue.

@magiconair magiconair added this to the 1.0.6 milestone Nov 27, 2015
@magiconair
Copy link
Contributor Author

Looks like there are some compatibility issues with the WS proxy that uses the library. I'll make the raw WS proxy the default in 1.0.6.

@magiconair
Copy link
Contributor Author

Merged raw websocket proxy to master as the default.

magiconair added a commit that referenced this issue Dec 1, 2015
Fixes #9: websocket support
Fixes #15: Traffic shaping does not match on service name
Fixes #16: Filter routing table not on tags
Fixes #18: Manage manual overrides via UI
@magiconair magiconair modified the milestones: 1.0.6, 1.0.5 Oct 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants